Skip to main content

amaru_kernel/cardano/
ballot.rs

1// Copyright 2025 PRAGMA
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{Anchor, Vote, cbor};
16
17#[derive(Clone, Debug, PartialEq)]
18pub struct Ballot {
19    vote: Vote,
20
21    /// NOTE: Anchors are kept in a Box since
22    ///
23    /// - Anchors are often absent from votes (~60% of the time on Mainnet); thus reducing the
24    ///   memory footprint of this from 64B to 16B
25    ///
26    /// - Rarely (i.e. never) accessed anyway by the ledger which completely neglects the cost of
27    ///   the indirection.
28    ///
29    /// One may ask: if never accessed, why keep it around?
30    ///
31    /// -> Because it is still on-chain data which may be relevant to users even though it has no
32    ///    influence on the ledger validations.
33    anchor: Option<Box<Anchor>>,
34}
35
36impl Ballot {
37    pub fn new(vote: Vote, anchor: Option<Anchor>) -> Self {
38        Self { vote, anchor: anchor.map(Box::new) }
39    }
40
41    pub fn vote(&self) -> &Vote {
42        &self.vote
43    }
44
45    pub fn anchor(&self) -> Option<&Anchor> {
46        self.anchor.as_deref()
47    }
48}
49
50impl<C> cbor::encode::Encode<C> for Ballot {
51    fn encode<W: cbor::encode::Write>(
52        &self,
53        e: &mut cbor::Encoder<W>,
54        ctx: &mut C,
55    ) -> Result<(), cbor::encode::Error<W::Error>> {
56        e.array(2)?;
57        e.encode_with(self.vote(), ctx)?;
58        e.encode_with(self.anchor(), ctx)?;
59        Ok(())
60    }
61}
62
63impl<'d, C> cbor::decode::Decode<'d, C> for Ballot {
64    fn decode(d: &mut cbor::Decoder<'d>, ctx: &mut C) -> Result<Self, cbor::decode::Error> {
65        cbor::heterogeneous_array(d, |d, assert_len| {
66            assert_len(2)?;
67            Ok(Self { vote: d.decode_with(ctx)?, anchor: d.decode_with(ctx)? })
68        })
69    }
70}
71
72#[cfg(any(test, feature = "test-utils"))]
73pub use tests::*;
74
75#[cfg(any(test, feature = "test-utils"))]
76mod tests {
77    use proptest::{option, prelude::*};
78
79    use super::Ballot;
80    use crate::{any_anchor, any_vote, prop_cbor_roundtrip};
81
82    prop_compose! {
83        pub fn any_ballot()(
84            vote in any_vote(),
85            anchor in option::of(any_anchor()),
86        ) -> Ballot  {
87            Ballot::new(vote, anchor)
88        }
89    }
90
91    prop_cbor_roundtrip!(Ballot, any_ballot());
92}