cardano_serialization_lib/builders/
voting_builder.rs1use 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 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}