cml_chain/builders/
proposal_builder.rs1use crate::{
2 crypto::hash::hash_plutus_data, governance::ProposalProcedure, plutus::PlutusData,
3 transaction::NativeScript, RequiredSigners,
4};
5
6use super::{
7 utils::required_wits_from_required_signers,
8 witness_builder::{
9 InputAggregateWitnessData, NativeScriptWitnessInfo, PartialPlutusWitness,
10 RequiredWitnessSet,
11 },
12};
13
14#[derive(Debug, thiserror::Error)]
15pub enum ProposalBuilderError {
16 #[error("Proposal uses script. Call with_plutus_proposal() instead.")]
17 ProposalIsScript,
18 #[error("Proposal uses key hash. Call with_proposal() instead.")]
19 ProposalIsKeyHash,
20 #[error("Missing the following witnesses for the input: {0:?}")]
21 MissingWitnesses(Box<RequiredWitnessSet>),
22}
23
24#[derive(Clone, Debug, Default)]
25pub struct ProposalBuilderResult {
26 pub proposals: Vec<ProposalProcedure>,
27 pub required_wits: RequiredWitnessSet,
28 pub aggregate_witnesses: Vec<InputAggregateWitnessData>,
29}
30
31#[derive(Clone, Debug)]
32pub struct ProposalBuilder {
33 result: ProposalBuilderResult,
34}
35
36impl Default for ProposalBuilder {
37 fn default() -> Self {
38 Self::new()
39 }
40}
41
42impl ProposalBuilder {
43 pub fn new() -> Self {
44 Self {
45 result: ProposalBuilderResult::default(),
46 }
47 }
48
49 pub fn with_proposal(
50 mut self,
51 proposal: ProposalProcedure,
52 ) -> Result<Self, ProposalBuilderError> {
53 if proposal.gov_action.script_hash().is_some() {
54 return Err(ProposalBuilderError::ProposalIsScript);
55 }
56
57 self.result.proposals.push(proposal.clone());
58
59 Ok(self)
60 }
61
62 pub fn with_native_script_proposal(
63 mut self,
64 proposal: ProposalProcedure,
65 native_script: NativeScript,
66 witness_info: NativeScriptWitnessInfo,
67 ) -> Result<Self, ProposalBuilderError> {
68 if let Some(script_hash) = proposal.gov_action.script_hash() {
69 if *script_hash != native_script.hash() {
70 let mut err_req_wits = RequiredWitnessSet::new();
71 err_req_wits.add_script_hash(*script_hash);
72 return Err(ProposalBuilderError::MissingWitnesses(Box::new(
73 err_req_wits,
74 )));
75 }
76 self.result.required_wits.add_script_hash(*script_hash);
77 } else {
78 return Err(ProposalBuilderError::ProposalIsKeyHash);
79 }
80
81 self.result.proposals.push(proposal);
82
83 self.result
84 .aggregate_witnesses
85 .push(InputAggregateWitnessData::NativeScript(
86 native_script,
87 witness_info,
88 ));
89
90 Ok(self)
91 }
92
93 pub fn with_plutus_proposal(
94 self,
95 proposal: ProposalProcedure,
96 partial_witness: PartialPlutusWitness,
97 required_signers: RequiredSigners,
98 datum: PlutusData,
99 ) -> Result<Self, ProposalBuilderError> {
100 self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, Some(datum))
101 }
102
103 pub fn with_plutus_proposal_inline_datum(
104 self,
105 proposal: ProposalProcedure,
106 partial_witness: PartialPlutusWitness,
107 required_signers: RequiredSigners,
108 ) -> Result<Self, ProposalBuilderError> {
109 self.with_plutus_proposal_impl(proposal, partial_witness, required_signers, None)
110 }
111
112 fn with_plutus_proposal_impl(
113 mut self,
114 proposal: ProposalProcedure,
115 partial_witness: PartialPlutusWitness,
116 required_signers: RequiredSigners,
117 datum: Option<PlutusData>,
118 ) -> Result<Self, ProposalBuilderError> {
119 let mut required_wits = required_wits_from_required_signers(&required_signers);
120 if let Some(script_hash) = proposal.gov_action.script_hash() {
121 required_wits.add_script_hash(*script_hash);
122 } else {
123 return Err(ProposalBuilderError::ProposalIsKeyHash);
124 }
125
126 let mut required_wits_left = required_wits.clone();
127
128 required_wits_left.vkeys.clear();
130
131 let script_hash = partial_witness.script.hash();
132
133 required_wits_left.scripts.remove(&script_hash);
135 if let Some(datum) = &datum {
136 required_wits_left
137 .plutus_data
138 .remove(&hash_plutus_data(datum));
139 }
140
141 if required_wits_left.len() > 0 {
142 return Err(ProposalBuilderError::MissingWitnesses(Box::new(
143 required_wits_left,
144 )));
145 }
146
147 self.result.proposals.push(proposal);
148
149 self.result.required_wits.add_all(required_wits);
150
151 self.result
152 .aggregate_witnesses
153 .push(InputAggregateWitnessData::PlutusScript(
154 partial_witness,
155 required_signers,
156 datum,
157 ));
158
159 Ok(self)
160 }
161
162 pub fn build(self) -> ProposalBuilderResult {
163 self.result
164 }
165}