cardano_serialization_lib/builders/
voting_builder.rs

1use crate::*;
2use std::collections::BTreeMap;
3
4#[derive(Clone, Debug)]
5struct VoterVotes {
6    script_witness: Option<ScriptWitnessType>,
7    votes: BTreeMap<GovernanceActionId, VotingProcedure>,
8}
9
10#[wasm_bindgen]
11#[derive(Clone, Debug)]
12pub struct VotingBuilder {
13    votes: BTreeMap<Voter, VoterVotes>,
14}
15
16#[wasm_bindgen]
17impl VotingBuilder {
18    pub fn new() -> Self {
19        Self {
20            votes: BTreeMap::new(),
21        }
22    }
23
24    pub fn add(
25        &mut self,
26        voter: &Voter,
27        gov_action_id: &GovernanceActionId,
28        voting_procedure: &VotingProcedure,
29    ) -> Result<(), JsError> {
30        if voter.has_script_credentials() {
31            return Err(JsError::from_str(
32                "Your voter has a required script witness.\
33                Please use .add_with_plutus_witness or .add_with_native_script instead.",
34            ));
35        }
36
37        let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes {
38            script_witness: None,
39            votes: BTreeMap::new(),
40        });
41
42        voter_votes
43            .votes
44            .insert(gov_action_id.clone(), voting_procedure.clone());
45
46        Ok(())
47    }
48
49    pub fn add_with_plutus_witness(
50        &mut self,
51        voter: &Voter,
52        gov_action_id: &GovernanceActionId,
53        voting_procedure: &VotingProcedure,
54        witness: &PlutusWitness,
55    ) -> Result<(), JsError> {
56        if !voter.has_script_credentials() {
57            return Err(JsError::from_str(
58                "Your voter does not have a required script witness.\
59                Please use .add instead.",
60            ));
61        }
62
63        let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes {
64            script_witness: Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())),
65            votes: BTreeMap::new(),
66        });
67
68        voter_votes
69            .votes
70            .insert(gov_action_id.clone(), voting_procedure.clone());
71
72        Ok(())
73    }
74
75    pub fn add_with_native_script(
76        &mut self,
77        voter: &Voter,
78        gov_action_id: &GovernanceActionId,
79        voting_procedure: &VotingProcedure,
80        native_script_source: &NativeScriptSource,
81    ) -> Result<(), JsError> {
82        if !voter.has_script_credentials() {
83            return Err(JsError::from_str(
84                "Your voter does not have a required script witness.\
85                Please use .add instead.",
86            ));
87        }
88
89        let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes {
90            script_witness: Some(ScriptWitnessType::NativeScriptWitness(
91                native_script_source.0.clone(),
92            )),
93            votes: BTreeMap::new(),
94        });
95
96        voter_votes
97            .votes
98            .insert(gov_action_id.clone(), voting_procedure.clone());
99
100        Ok(())
101    }
102
103    pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes {
104        let mut set = Ed25519KeyHashes::new();
105        for (voter, voter_votes) in &self.votes {
106            let req_signature = voter.to_key_hash();
107            if let Some(req_signature) = req_signature {
108                set.add_move(req_signature);
109            }
110
111            if let Some(ScriptWitnessType::NativeScriptWitness(script_source)) =
112                &voter_votes.script_witness
113            {
114                if let Some(required_signers) = script_source.required_signers() {
115                    set.extend_move(required_signers);
116                }
117            }
118        }
119        set
120    }
121
122    pub fn get_plutus_witnesses(&self) -> PlutusWitnesses {
123        let tag = RedeemerTag::new_vote();
124        let mut scripts = PlutusWitnesses::new();
125        for (i, (_, voter_votes)) in self.votes.iter().enumerate() {
126            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = &voter_votes.script_witness {
127                let index = BigNum::from(i);
128                scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag));
129            }
130        }
131        scripts
132    }
133
134    pub fn get_ref_inputs(&self) -> TransactionInputs {
135        let mut inputs = Vec::new();
136        for (_, voter_votes) in &self.votes {
137            match &voter_votes.script_witness {
138                Some(script_witness) => {
139                    if let Some(input) = script_witness.get_script_ref_input() {
140                        inputs.push(input);
141                    }
142                    if let Some(input) = script_witness.get_datum_ref_input() {
143                        inputs.push(input);
144                    }
145                }
146                None => {}
147            }
148        }
149        TransactionInputs::from_vec(inputs)
150    }
151
152    pub fn get_native_scripts(&self) -> NativeScripts {
153        let mut scripts = NativeScripts::new();
154        for (_, voter_votes) in &self.votes {
155            if let Some(ScriptWitnessType::NativeScriptWitness(
156                NativeScriptSourceEnum::NativeScript(script, _),
157            )) = &voter_votes.script_witness
158            {
159                scripts.add(script);
160            }
161        }
162        scripts
163    }
164
165    pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet<Language> {
166        let mut used_langs = BTreeSet::new();
167        for (_, voter_votes) in &self.votes {
168            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = &voter_votes.script_witness {
169                used_langs.insert(s.script.language());
170            }
171        }
172        used_langs
173    }
174
175    //return only ref inputs that are script refs with added size
176    //used for calculating the fee for the transaction
177    //another ref input and also script ref input without size are filtered out
178    pub(crate) fn get_script_ref_inputs_with_size(
179        &self,
180    ) -> impl Iterator<Item = (&TransactionInput, usize)> {
181        self.votes.iter()
182            .filter_map(|(_, voter_votes)| voter_votes.script_witness.as_ref())
183            .filter_map(|script_witness| script_witness.get_script_ref_input_with_size())
184    }
185
186    pub fn has_plutus_scripts(&self) -> bool {
187        for (_, voter_votes) in &self.votes {
188            if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = voter_votes.script_witness {
189                return true;
190            }
191        }
192        false
193    }
194
195    pub fn build(&self) -> VotingProcedures {
196        let mut voters = BTreeMap::new();
197        for (voter, voter_votes) in &self.votes {
198            let mut votes = BTreeMap::new();
199            for (action, voting_procedure) in &voter_votes.votes {
200                votes.insert(action.clone(), voting_procedure.clone());
201            }
202            voters.insert(voter.clone(), votes);
203        }
204        VotingProcedures(voters)
205    }
206}