-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rust/vote-tx-v2): Feat add initial CBOR decoding/encoding implem…
…entation of the generalised vote tx (#81) * add vote-tx-v2 crate * rename jormungandr-vote-tx to vote-tx-v1 * add Vote CBOR encoding/decoding * wip * wip * wip * wip * add proptest for Vote type * replace ciborium usage with minicbor * add new TxBody struct, cleanup gen_vote_tx.cddl * add TxBody CBOR decoding/encoding impl * add Cbor trait * add GeneralisedTx struct * wip * wip * fix spelling * fix doctest * add array lenth validation
- Loading branch information
Showing
12 changed files
with
448 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
rust/jormungandr-vote-tx/Cargo.toml → rust/vote-tx-v1/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
[package] | ||
name = "vote-tx-v2" | ||
version = "0.1.0" | ||
edition.workspace = true | ||
authors.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
license.workspace = true | ||
|
||
[lib] | ||
crate-type = ["lib", "cdylib"] | ||
|
||
[lints] | ||
workspace = true | ||
|
||
[dependencies] | ||
anyhow = "1.0.89" | ||
proptest = { version = "1.5.0" } | ||
minicbor = { version = "0.25.1", features = ["alloc"] } | ||
|
||
[dev-dependencies] | ||
# Potentially it could be replaced with using `proptest::property_test` attribute macro, | ||
# after this PR will be merged https://github.com/proptest-rs/proptest/pull/523 | ||
test-strategy = "0.4.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
//! CBOR encoding and decoding implementation. | ||
//! <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/catalyst_voting/cddl/gen_vote_tx.cddl> | ||
use minicbor::{ | ||
data::{IanaTag, Tag}, | ||
Decode, Decoder, Encode, Encoder, | ||
}; | ||
|
||
use crate::{Choice, GeneralizedTx, Proof, PropId, TxBody, Uuid, Vote, VoterData}; | ||
|
||
/// UUID CBOR tag <https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml/>. | ||
const CBOR_UUID_TAG: u64 = 37; | ||
|
||
/// `Vote` array struct length | ||
const VOTE_LEN: u64 = 3; | ||
|
||
/// `TxBody` array struct length | ||
const TX_BODY_LEN: u64 = 3; | ||
|
||
/// `GeneralizedTx` array struct length | ||
const GENERALIZED_TX_LEN: u64 = 1; | ||
|
||
impl Decode<'_, ()> for GeneralizedTx { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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 ())?; | ||
Ok(Self { tx_body }) | ||
} | ||
} | ||
|
||
impl Encode<()> for GeneralizedTx { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.array(GENERALIZED_TX_LEN)?; | ||
self.tx_body.encode(e, &mut ())?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for TxBody { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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 votes = Vec::<Vote>::decode(d, &mut ())?; | ||
let voter_data = VoterData::decode(d, &mut ())?; | ||
Ok(Self { | ||
vote_type, | ||
votes, | ||
voter_data, | ||
}) | ||
} | ||
} | ||
|
||
impl Encode<()> for TxBody { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.array(TX_BODY_LEN)?; | ||
self.vote_type.encode(e, &mut ())?; | ||
self.votes.encode(e, &mut ())?; | ||
self.voter_data.encode(e, &mut ())?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for VoterData { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.tag(IanaTag::Cbor.tag())?; | ||
e.bytes(&self.0)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for Uuid { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.tag(Tag::new(CBOR_UUID_TAG))?; | ||
e.bytes(&self.0)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for Vote { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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::<Choice>::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 { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::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 { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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 Choice { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.tag(IanaTag::Cbor.tag())?; | ||
e.bytes(&self.0)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for Proof { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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 Proof { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.tag(IanaTag::Cbor.tag())?; | ||
e.bytes(&self.0)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
impl Decode<'_, ()> for PropId { | ||
fn decode(d: &mut Decoder<'_>, (): &mut ()) -> Result<Self, minicbor::decode::Error> { | ||
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 choice = d.bytes()?.to_vec(); | ||
Ok(Self(choice)) | ||
} | ||
} | ||
|
||
impl Encode<()> for PropId { | ||
fn encode<W: minicbor::encode::Write>( | ||
&self, e: &mut minicbor::Encoder<W>, (): &mut (), | ||
) -> Result<(), minicbor::encode::Error<W::Error>> { | ||
e.tag(IanaTag::Cbor.tag())?; | ||
e.bytes(&self.0)?; | ||
Ok(()) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use test_strategy::proptest; | ||
|
||
use super::*; | ||
use crate::Cbor; | ||
|
||
#[proptest] | ||
fn generalized_tx_from_bytes_to_bytes_test(generalized_tx: GeneralizedTx) { | ||
let bytes = generalized_tx.to_bytes().unwrap(); | ||
let decoded = GeneralizedTx::from_bytes(&bytes).unwrap(); | ||
assert_eq!(generalized_tx, decoded); | ||
} | ||
|
||
#[proptest] | ||
fn tx_body_from_bytes_to_bytes_test(tx_body: TxBody) { | ||
let bytes = tx_body.to_bytes().unwrap(); | ||
let decoded = TxBody::from_bytes(&bytes).unwrap(); | ||
assert_eq!(tx_body, decoded); | ||
} | ||
|
||
#[proptest] | ||
fn vote_from_bytes_to_bytes_test(vote: Vote) { | ||
let bytes = vote.to_bytes().unwrap(); | ||
let decoded = Vote::from_bytes(&bytes).unwrap(); | ||
assert_eq!(vote, decoded); | ||
} | ||
} |
Oops, something went wrong.