cml_chain/builders/
witness_builder.rs

1use linked_hash_map::LinkedHashMap;
2use std::{
3    collections::{BTreeSet, HashMap},
4    fmt::Debug,
5};
6
7use crate::{
8    byron::ByronAddress,
9    certs::Credential,
10    crypto::{hash::hash_plutus_data, BootstrapWitness, Vkey, Vkeywitness},
11    plutus::{
12        LegacyRedeemer, PlutusData, PlutusScript, PlutusV1Script, PlutusV2Script, PlutusV3Script,
13        Redeemers,
14    },
15    transaction::TransactionWitnessSet,
16    NativeScript, RequiredSigners, Script,
17};
18use cml_crypto::{
19    DatumHash, Ed25519KeyHash, Ed25519Signature, PublicKey, RawBytesEncoding, ScriptHash,
20};
21
22use super::{
23    redeemer_builder::{MissingExunitError, RedeemerBuilderError, RedeemerWitnessKey},
24    tx_builder::TransactionUnspentOutput,
25};
26
27#[derive(Debug, thiserror::Error)]
28pub enum WitnessBuilderError {
29    #[error("Missing the following witnesses: {0:?}")]
30    MissingWitnesses(RequiredWitnessSet),
31    #[error("Missing ExUnit: {0}")]
32    MissingExUnit(#[from] MissingExunitError),
33    #[error("LegacyRedeemer build failed: {0}")]
34    RedeemBuildFailed(#[from] RedeemerBuilderError),
35}
36
37#[derive(Debug, Clone)] //, Eq, Ord, PartialEq, PartialOrd)]
38pub enum PlutusScriptWitness {
39    Ref(ScriptHash),
40    Script(PlutusScript),
41}
42
43impl PlutusScriptWitness {
44    // pub fn script(&self) -> Option<PlutusScript> {
45    //     match self {
46    //         Self::Ref(_) => None,
47    //         Self::Script(script) => Some(script.clone()),
48    //     }
49    // }
50
51    pub fn hash(&self) -> ScriptHash {
52        match self {
53            Self::Ref(hash) => *hash,
54            Self::Script(script) => script.hash(),
55        }
56    }
57}
58
59impl From<PlutusScript> for PlutusScriptWitness {
60    fn from(script: PlutusScript) -> Self {
61        PlutusScriptWitness::Script(script)
62    }
63}
64
65impl From<ScriptHash> for PlutusScriptWitness {
66    fn from(hash: ScriptHash) -> Self {
67        PlutusScriptWitness::Ref(hash)
68    }
69}
70
71/// A partial Plutus witness
72/// It contains all the information needed to witness the Plutus script execution
73/// except for the redeemer tag and index
74/// Note: no datum is attached because only input script types have datums
75#[derive(Clone, Debug)]
76pub struct PartialPlutusWitness {
77    pub script: PlutusScriptWitness,
78    pub redeemer: PlutusData,
79}
80
81impl PartialPlutusWitness {
82    pub fn new(script: PlutusScriptWitness, redeemer: PlutusData) -> Self {
83        Self { script, redeemer }
84    }
85}
86
87#[derive(Clone, Debug)]
88pub enum InputAggregateWitnessData {
89    NativeScript(NativeScript, NativeScriptWitnessInfo),
90    PlutusScript(PartialPlutusWitness, RequiredSigners, Option<PlutusData>),
91}
92
93impl InputAggregateWitnessData {
94    pub fn redeemer_plutus_data(&self) -> Option<&PlutusData> {
95        match self {
96            InputAggregateWitnessData::PlutusScript(witness, _, _) => Some(&witness.redeemer),
97            _ => None,
98        }
99    }
100}
101
102#[derive(Clone, Debug, Default)]
103pub struct RequiredWitnessSet {
104    // note: the real key type for these is Vkey
105    // but cryptographically these should be equivalent and Ed25519KeyHash is more flexible
106    pub vkeys: BTreeSet<Ed25519KeyHash>,
107    // note: the real key type for these is Vkey
108    // but cryptographically these should be equivalent as ByronAddress contains an AddressId
109    // which is the hash of data that includes the public key
110    pub bootstraps: BTreeSet<ByronAddress>,
111    // note: no way to differentiate Plutus script from native script
112    pub scripts: BTreeSet<ScriptHash>,
113    pub plutus_data: BTreeSet<DatumHash>,
114    pub redeemers: BTreeSet<RedeemerWitnessKey>,
115    pub script_refs: BTreeSet<ScriptHash>,
116}
117
118impl RequiredWitnessSet {
119    // pub fn add_vkey(&mut self, vkey: &Vkeywitness) {
120    //     self.add_vkey_key(&vkey.vkey());
121    // }
122    // pub fn add_vkey_key(&mut self, vkey: &Vkey) {
123    //     self.add_vkey_key_hash(&vkey.public_key().hash());
124    // }
125    pub fn add_vkey_key_hash(&mut self, hash: Ed25519KeyHash) {
126        self.vkeys.insert(hash);
127    }
128
129    pub fn add_bootstrap(&mut self, address: ByronAddress) {
130        self.bootstraps.insert(address);
131    }
132
133    pub fn add_script_ref(&mut self, script_hash: ScriptHash) {
134        self.scripts.remove(&script_hash);
135        self.script_refs.insert(script_hash);
136    }
137
138    // pub fn add_native_script(&mut self, native_script: &NativeScript) {
139    //     self.add_script_hash(&native_script.hash());
140    // }
141
142    pub fn add_script_hash(&mut self, script_hash: ScriptHash) {
143        match self.script_refs.get(&script_hash) {
144            None => {
145                self.scripts.insert(script_hash);
146            }
147            Some(_) => {}
148        }
149    }
150
151    pub(crate) fn add_from_credential(&mut self, credential: Credential) {
152        match credential {
153            Credential::PubKey { hash, .. } => self.add_vkey_key_hash(hash),
154            Credential::Script { hash, .. } => self.add_script_hash(hash),
155        }
156    }
157
158    // pub fn add_plutus_script(&mut self, plutus_v1_script: &PlutusScript) {
159    //     self.add_script_hash(&plutus_v1_script.hash());
160    // }
161
162    // pub fn add_plutus_datum(&mut self, plutus_datum: &PlutusData) {
163    //     self.add_plutus_datum_hash(&hash_plutus_data(plutus_datum));
164    // }
165    pub fn add_plutus_datum_hash(&mut self, plutus_datum: DatumHash) {
166        self.plutus_data.insert(plutus_datum);
167    }
168
169    // pub fn add_redeemer(&mut self, redeemer: &LegacyRedeemer) {
170    //     self.add_redeemer_tag(&RedeemerWitnessKey::new(&redeemer.tag(), &redeemer.index()));
171    // }
172    pub fn add_redeemer_tag(&mut self, redeemer: RedeemerWitnessKey) {
173        self.redeemers.insert(redeemer);
174    }
175
176    pub fn add_all(&mut self, requirements: RequiredWitnessSet) {
177        self.vkeys.extend(requirements.vkeys);
178        self.bootstraps.extend(requirements.bootstraps);
179        self.scripts.extend(requirements.scripts);
180        self.plutus_data.extend(requirements.plutus_data);
181        self.redeemers.extend(requirements.redeemers);
182    }
183
184    pub fn remove_ref_scripts(&mut self, ref_inputs: &[TransactionUnspentOutput]) {
185        ref_inputs.iter().for_each(|utxo| {
186            utxo.output.script_ref().inspect(|script_ref| {
187                self.scripts.remove(&script_ref.hash());
188            });
189        })
190    }
191
192    pub(crate) fn len(&self) -> usize {
193        self.vkeys.len()
194            + self.bootstraps.len()
195            + self.scripts.len()
196            + self.plutus_data.len()
197            + self.redeemers.len()
198    }
199
200    // This method add fake vkeys to calculate the fee.
201    // In order to prevent that fake keys get deduplicated when it is called more than once,
202    // its index starts from the current amount of vkeys.
203    // WARN: this function might fail at runtime when there are more than 255 witnesses,
204    // however this is unrealistic because the limit of transaction size. (101 bytes each witness)
205    pub(crate) fn add_fake_vkey_witnesses_by_num(&mut self, num: usize) {
206        for _ in 0..num {
207            self.add_vkey_key_hash(fake_key_hash(self.vkeys.len() as u8));
208        }
209    }
210
211    pub(crate) fn add_input_aggregate_fake_witness_data(
212        &mut self,
213        data: &InputAggregateWitnessData,
214    ) {
215        match data {
216            InputAggregateWitnessData::NativeScript(script, info) => {
217                match info {
218                    NativeScriptWitnessInfo::Count(num) => {
219                        self.add_fake_vkey_witnesses_by_num(*num)
220                    }
221                    NativeScriptWitnessInfo::Vkeys(ref vkeys) => {
222                        vkeys
223                            .iter()
224                            .cloned()
225                            .for_each(|vkey| self.add_vkey_key_hash(vkey));
226                    }
227                    NativeScriptWitnessInfo::AssumeWorst => {
228                        // we get the size instead of the hashes themselves
229                        // since there is no way to know if any of these will actually be required to sign the tx
230                        let num = script.get_required_signers().len();
231                        self.add_fake_vkey_witnesses_by_num(num);
232                    }
233                }
234            }
235            InputAggregateWitnessData::PlutusScript(_witness, required_signers, _option) => {
236                required_signers
237                    .iter()
238                    .cloned()
239                    .for_each(|vkey| self.add_vkey_key_hash(vkey));
240            }
241        }
242    }
243
244    pub fn new() -> Self {
245        // have to expose new so it's visible in WASM
246        Self::default()
247    }
248}
249
250/// Builder de-duplicates witnesses as they are added
251#[derive(Clone, Default, Debug)]
252pub struct TransactionWitnessSetBuilder {
253    // See Alonzo spec section 3.1 which defines the keys for these types
254    pub vkeys: HashMap<Vkey, Vkeywitness>,
255    pub bootstraps: HashMap<Vkey, BootstrapWitness>,
256    pub scripts: HashMap<ScriptHash, Script>,
257    pub plutus_data: LinkedHashMap<DatumHash, PlutusData>,
258    pub redeemers: LinkedHashMap<RedeemerWitnessKey, LegacyRedeemer>,
259
260    /// witnesses that need to be added for the build function to succeed
261    /// this allows checking that witnesses are present at build time (instead of when submitting to a node)
262    /// This is useful for APIs that can keep track of which witnesses will be required (like transaction builders)
263    pub required_wits: RequiredWitnessSet,
264}
265
266impl TransactionWitnessSetBuilder {
267    // pub fn get_vkeys(&self) -> Vkeys {
268    //     Vkeys(self.vkeys.clone().into_keys().collect())
269    // }
270
271    pub fn add_vkey(&mut self, vkey_witness: Vkeywitness) {
272        let vkey = vkey_witness.vkey.clone();
273        self.vkeys.insert(vkey, vkey_witness);
274    }
275
276    pub fn add_bootstrap(&mut self, bootstrap: BootstrapWitness) {
277        self.bootstraps
278            .insert(bootstrap.public_key.clone(), bootstrap);
279    }
280
281    // pub fn get_bootstraps(&self) -> Vkeys {
282    //     Vkeys(self.bootstraps.clone().into_keys().collect())
283    // }
284
285    pub fn add_script(&mut self, script: Script) {
286        self.scripts.insert(script.hash(), script);
287    }
288
289    pub fn get_native_script(&self) -> Vec<NativeScript> {
290        self.scripts
291            .iter()
292            .filter(|entry| !self.required_wits.script_refs.contains(entry.0))
293            .fold(
294                Vec::<NativeScript>::new(),
295                |mut acc, script| match &script.1 {
296                    Script::Native { script, .. } => {
297                        acc.push(script.clone());
298                        acc
299                    }
300                    _ => acc,
301                },
302            )
303    }
304
305    pub fn get_plutus_v1_script(&self) -> Vec<PlutusV1Script> {
306        self.scripts
307            .iter()
308            .filter(|entry| !self.required_wits.script_refs.contains(entry.0))
309            .fold(
310                Vec::<PlutusV1Script>::new(),
311                |mut acc, script| match &script.1 {
312                    Script::PlutusV1 { script, .. } => {
313                        acc.push(script.clone());
314                        acc
315                    }
316                    _ => acc,
317                },
318            )
319    }
320
321    pub fn get_plutus_v2_script(&self) -> Vec<PlutusV2Script> {
322        self.scripts
323            .iter()
324            .filter(|entry| !self.required_wits.script_refs.contains(entry.0))
325            .fold(
326                Vec::<PlutusV2Script>::new(),
327                |mut acc, script| match &script.1 {
328                    &Script::PlutusV2 { script, .. } => {
329                        acc.push(script.clone());
330                        acc
331                    }
332                    _ => acc,
333                },
334            )
335    }
336
337    pub fn get_plutus_v3_script(&self) -> Vec<PlutusV3Script> {
338        self.scripts
339            .iter()
340            .filter(|entry| !self.required_wits.script_refs.contains(entry.0))
341            .fold(
342                Vec::<PlutusV3Script>::new(),
343                |mut acc, script| match &script.1 {
344                    &Script::PlutusV3 { script, .. } => {
345                        acc.push(script.clone());
346                        acc
347                    }
348                    _ => acc,
349                },
350            )
351    }
352
353    pub fn add_plutus_datum(&mut self, plutus_datum: PlutusData) {
354        self.plutus_data
355            .insert(hash_plutus_data(&plutus_datum), plutus_datum);
356    }
357
358    pub fn get_plutus_datum(&self) -> Vec<PlutusData> {
359        self.plutus_data.values().cloned().collect()
360    }
361
362    pub fn add_redeemer(&mut self, redeemer: LegacyRedeemer) {
363        self.redeemers
364            .insert(RedeemerWitnessKey::from(&redeemer), redeemer);
365    }
366
367    pub fn get_redeemer(&self) -> Vec<LegacyRedeemer> {
368        self.redeemers.values().cloned().collect()
369    }
370
371    pub fn add_required_wits(&mut self, required_wits: RequiredWitnessSet) {
372        self.required_wits.add_all(required_wits)
373    }
374
375    pub fn new() -> Self {
376        // have to expose new so it's visible in WASM
377        Self::default()
378    }
379
380    pub fn add_existing(&mut self, wit_set: TransactionWitnessSet) {
381        if let Some(vkeys) = wit_set.vkeywitnesses {
382            vkeys.iter().for_each(|vkey| {
383                self.add_vkey(vkey.clone());
384            });
385        }
386        if let Some(bootstraps) = wit_set.bootstrap_witnesses {
387            bootstraps.iter().for_each(|bootstrap| {
388                self.add_bootstrap(bootstrap.clone());
389            });
390        }
391        if let Some(native_scripts) = wit_set.native_scripts {
392            native_scripts.iter().for_each(|native_script| {
393                self.add_script(native_script.clone().into());
394            });
395        }
396        if let Some(plutus_scripts) = wit_set.plutus_v1_scripts {
397            plutus_scripts.iter().for_each(|plutus_script| {
398                self.add_script(plutus_script.clone().into());
399            });
400        }
401        if let Some(plutus_scripts) = wit_set.plutus_v2_scripts {
402            plutus_scripts.iter().for_each(|plutus_script| {
403                self.add_script(plutus_script.clone().into());
404            });
405        }
406        if let Some(plutus_scripts) = wit_set.plutus_v3_scripts {
407            plutus_scripts.iter().for_each(|plutus_script| {
408                self.add_script(plutus_script.clone().into());
409            });
410        }
411        if let Some(redeemers) = wit_set.redeemers {
412            redeemers.to_flat_format().into_iter().for_each(|redeemer| {
413                self.add_redeemer(redeemer);
414            });
415        }
416        if let Some(plutus_datums) = wit_set.plutus_datums {
417            plutus_datums.iter().for_each(|plutus_datum| {
418                self.add_plutus_datum(plutus_datum.clone());
419            });
420        }
421    }
422
423    pub(crate) fn add_input_aggregate_real_witness_data(
424        &mut self,
425        data: &InputAggregateWitnessData,
426    ) {
427        match data {
428            InputAggregateWitnessData::NativeScript(script, _info) => {
429                self.add_script(script.clone().into());
430            }
431            InputAggregateWitnessData::PlutusScript(witness, _info, option) => {
432                match &witness.script {
433                    PlutusScriptWitness::Script(plutus_script) => {
434                        self.add_script(plutus_script.clone().into());
435                    }
436                    PlutusScriptWitness::Ref(_) => {
437                        // We don't add the script references to the witness set
438                    }
439                }
440                if let Some(data) = option {
441                    self.add_plutus_datum(data.clone());
442                }
443            }
444        }
445    }
446
447    pub fn build(self) -> TransactionWitnessSet {
448        let mut result = TransactionWitnessSet::new();
449        let native_scripts = self.get_native_script();
450        let plutus_v1_scripts = self.get_plutus_v1_script();
451        let plutus_v2_scripts = self.get_plutus_v2_script();
452        let plutus_v3_scripts = self.get_plutus_v3_script();
453        let plutus_datums = self.get_plutus_datum();
454
455        if !self.vkeys.is_empty() {
456            result.vkeywitnesses = Some(self.vkeys.into_values().collect::<Vec<_>>().into());
457        }
458
459        if !self.bootstraps.is_empty() {
460            result.bootstrap_witnesses =
461                Some(self.bootstraps.into_values().collect::<Vec<_>>().into());
462        }
463
464        if !native_scripts.is_empty() {
465            result.native_scripts = Some(native_scripts.into());
466        }
467
468        if !plutus_v1_scripts.is_empty() {
469            result.plutus_v1_scripts = Some(plutus_v1_scripts.into());
470        }
471
472        if !plutus_v2_scripts.is_empty() {
473            result.plutus_v2_scripts = Some(plutus_v2_scripts.into());
474        }
475
476        if !plutus_v3_scripts.is_empty() {
477            result.plutus_v3_scripts = Some(plutus_v3_scripts.into());
478        }
479
480        if !self.plutus_data.is_empty() {
481            result.plutus_datums = Some(plutus_datums.into());
482        }
483
484        if !self.redeemers.is_empty() {
485            result.redeemers = Some(Redeemers::new_arr_legacy_redeemer(
486                self.redeemers.values().cloned().collect::<Vec<_>>(),
487            ));
488        }
489
490        result
491    }
492
493    pub fn remaining_wits(&self) -> RequiredWitnessSet {
494        let mut remaining_wits = self.required_wits.clone();
495
496        self.vkeys.keys().for_each(|key| {
497            remaining_wits.vkeys.remove(&key.hash());
498        });
499        self.bootstraps.values().for_each(|wit| {
500            remaining_wits
501                .bootstraps
502                .remove(&wit.to_address().unwrap().to_address());
503        });
504        self.scripts.keys().for_each(|hash| {
505            remaining_wits.scripts.remove(hash);
506        });
507        self.plutus_data.keys().for_each(|hash| {
508            remaining_wits.plutus_data.remove(hash);
509        });
510        self.redeemers.keys().for_each(|key| {
511            remaining_wits.redeemers.remove(key);
512        });
513
514        remaining_wits
515    }
516
517    pub fn try_build(&self) -> Result<TransactionWitnessSet, WitnessBuilderError> {
518        let remaining_wits = self.remaining_wits();
519
520        if remaining_wits.len() > 0 {
521            return Err(WitnessBuilderError::MissingWitnesses(remaining_wits));
522        }
523
524        Ok(self.clone().build())
525    }
526}
527
528pub fn merge_fake_witness(
529    builder: &mut TransactionWitnessSetBuilder,
530    required_wits: &RequiredWitnessSet,
531) {
532    let mut remaining_wits = required_wits.clone();
533    builder.vkeys.keys().for_each(|key| {
534        remaining_wits.vkeys.remove(&key.hash());
535    });
536    builder.bootstraps.values().for_each(|wit| {
537        remaining_wits
538            .bootstraps
539            .remove(&wit.to_address().unwrap().to_address());
540    });
541
542    // Ed25519KeyHash and AddressId are both (under no collision assumption) 1-1 mapping to the real keys
543    // so if all we care about is counting the number of witnesses,
544    // we can convert them to fake witnesses that just pad a dummy prefix to their 28byte size to get them to 32 bytes
545    let fake_prefix = [0u8; 4];
546
547    for remaining_vkey in remaining_wits.vkeys.iter() {
548        let fake_vkey =
549            PublicKey::from_raw_bytes(&[&fake_prefix, remaining_vkey.to_raw_bytes()].concat())
550                .unwrap();
551        let fake_sig = fake_raw_key_sig(0);
552        let fake_vkey_witness = Vkeywitness::new(fake_vkey, fake_sig);
553
554        // avoid accidentally overriding real witness
555        if !builder.vkeys.contains_key(&fake_vkey_witness.vkey) {
556            builder.add_vkey(fake_vkey_witness);
557        }
558    }
559    for remaining_bootstrap in remaining_wits.bootstraps.iter() {
560        let address_content = &remaining_bootstrap.content;
561        let fake_vkey = PublicKey::from_raw_bytes(
562            &[&fake_prefix, address_content.address_id.to_raw_bytes()].concat(),
563        )
564        .unwrap();
565        let fake_sig = fake_raw_key_sig(0);
566        let fake_chaincode = [0u8; 32]; // constant size so it won't affect the fee calculation
567        let fake_witness = BootstrapWitness::new(
568            fake_vkey,
569            fake_sig,
570            fake_chaincode.to_vec(),
571            address_content.addr_attributes.clone(),
572        )
573        .unwrap();
574
575        // avoid accidentally overriding real witness
576        if !builder.bootstraps.contains_key(&fake_witness.public_key) {
577            builder.add_bootstrap(fake_witness);
578        }
579    }
580}
581
582fn fake_raw_key_sig(id: u8) -> Ed25519Signature {
583    Ed25519Signature::from_raw_bytes(&[
584        id, 248, 153, 211, 155, 23, 253, 93, 102, 193, 146, 196, 181, 13, 52, 62, 66, 247, 35, 91,
585        48, 80, 76, 138, 231, 97, 159, 147, 200, 40, 220, 109, 206, 69, 104, 221, 105, 23, 124, 85,
586        24, 40, 73, 45, 119, 122, 103, 39, 253, 102, 194, 251, 204, 189, 168, 194, 174, 237, 146,
587        3, 44, 153, 121, 10,
588    ])
589    .unwrap()
590}
591
592fn fake_key_hash(x: u8) -> Ed25519KeyHash {
593    Ed25519KeyHash::from_raw_bytes(&[
594        x, 239, 181, 120, 142, 135, 19, 200, 68, 223, 211, 43, 46, 145, 222, 30, 48, 159, 239, 255,
595        213, 85, 248, 39, 204, 158, 225, 100,
596    ])
597    .unwrap()
598}
599
600#[derive(Clone, Debug)]
601pub enum NativeScriptWitnessInfo {
602    Count(usize),
603    Vkeys(Vec<Ed25519KeyHash>),
604    AssumeWorst,
605}
606
607impl NativeScriptWitnessInfo {
608    /// Unsure which keys will sign, but you know the exact number to save on tx fee
609    pub fn num_signatures(num: usize) -> Self {
610        NativeScriptWitnessInfo::Count(num)
611    }
612
613    /// This native script will be witnessed by exactly these keys
614    pub fn vkeys(vkeys: Vec<Ed25519KeyHash>) -> Self {
615        NativeScriptWitnessInfo::Vkeys(vkeys)
616    }
617
618    /// You don't know how many keys will sign, so the maximum possible case will be assumed
619    pub fn assume_signature_count() -> Self {
620        NativeScriptWitnessInfo::AssumeWorst
621    }
622}
623
624#[cfg(test)]
625mod tests {
626    use cml_crypto::{Bip32PrivateKey, Deserialize, Serialize, TransactionHash};
627
628    use crate::byron::make_icarus_bootstrap_witness;
629
630    use super::*;
631
632    fn fake_raw_key_public(id: u8) -> PublicKey {
633        PublicKey::from_raw_bytes(&[
634            id, 118, 57, 154, 33, 13, 232, 114, 14, 159, 168, 148, 228, 94, 65, 226, 154, 181, 37,
635            227, 11, 196, 2, 128, 28, 7, 98, 80, 209, 88, 91, 205,
636        ])
637        .unwrap()
638    }
639
640    fn fake_private_key1() -> Bip32PrivateKey {
641        Bip32PrivateKey::from_raw_bytes(&[
642            0xb8, 0xf2, 0xbe, 0xce, 0x9b, 0xdf, 0xe2, 0xb0, 0x28, 0x2f, 0x5b, 0xad, 0x70, 0x55,
643            0x62, 0xac, 0x99, 0x6e, 0xfb, 0x6a, 0xf9, 0x6b, 0x64, 0x8f, 0x44, 0x45, 0xec, 0x44,
644            0xf4, 0x7a, 0xd9, 0x5c, 0x10, 0xe3, 0xd7, 0x2f, 0x26, 0xed, 0x07, 0x54, 0x22, 0xa3,
645            0x6e, 0xd8, 0x58, 0x5c, 0x74, 0x5a, 0x0e, 0x11, 0x50, 0xbc, 0xce, 0xba, 0x23, 0x57,
646            0xd0, 0x58, 0x63, 0x69, 0x91, 0xf3, 0x8a, 0x37, 0x91, 0xe2, 0x48, 0xde, 0x50, 0x9c,
647            0x07, 0x0d, 0x81, 0x2a, 0xb2, 0xfd, 0xa5, 0x78, 0x60, 0xac, 0x87, 0x6b, 0xc4, 0x89,
648            0x19, 0x2c, 0x1e, 0xf4, 0xce, 0x25, 0x3c, 0x19, 0x7e, 0xe2, 0x19, 0xa4,
649        ])
650        .unwrap()
651    }
652
653    fn fake_private_key2() -> Bip32PrivateKey {
654        Bip32PrivateKey::from_raw_bytes(
655            &hex::decode("d84c65426109a36edda5375ea67f1b738e1dacf8629f2bb5a2b0b20f3cd5075873bf5cdfa7e533482677219ac7d639e30a38e2e645ea9140855f44ff09e60c52c8b95d0d35fe75a70f9f5633a3e2439b2994b9e2bc851c49e9f91d1a5dcbb1a3").unwrap()
656        ).unwrap()
657    }
658
659    #[test]
660    fn test_add_fake_vkey_witnesses_by_num() {
661        let mut builder = RequiredWitnessSet::new();
662        builder.add_fake_vkey_witnesses_by_num(2);
663        assert_eq!(builder.vkeys.len(), 2);
664        builder.add_fake_vkey_witnesses_by_num(1);
665        assert_eq!(builder.vkeys.len(), 3);
666    }
667
668    #[test]
669    fn test_add_input_aggregate_witness_data() {
670        let mut required_wits = RequiredWitnessSet::new();
671        let data = {
672            let witness = {
673                let script = PlutusScript::PlutusV1(PlutusV1Script::new(vec![0]));
674                PartialPlutusWitness {
675                    script: PlutusScriptWitness::Script(script),
676                    redeemer: PlutusData::new_integer(0u64.into()),
677                }
678            };
679            let missing_signers = vec![fake_raw_key_public(0).hash()];
680            InputAggregateWitnessData::PlutusScript(witness, missing_signers.into(), None)
681        };
682
683        assert_eq!(required_wits.vkeys.len(), 0);
684        required_wits.add_input_aggregate_fake_witness_data(&data);
685        assert_eq!(required_wits.vkeys.len(), 1);
686    }
687
688    #[test]
689    fn test_add_input_aggregate_witness_data_with_existing_key_hash() {
690        let mut required_wits = RequiredWitnessSet::new();
691        let key = fake_raw_key_public(0);
692        let hash = key.hash();
693        required_wits.add_vkey_key_hash(hash);
694
695        let data = {
696            let witness = {
697                let script = PlutusScript::PlutusV1(PlutusV1Script::new(vec![0]));
698                PartialPlutusWitness {
699                    script: PlutusScriptWitness::Script(script),
700                    redeemer: PlutusData::new_integer(0u64.into()),
701                }
702            };
703            let missing_signers = vec![hash];
704            InputAggregateWitnessData::PlutusScript(witness, missing_signers.into(), None)
705        };
706
707        assert_eq!(required_wits.vkeys.len(), 1);
708        required_wits.add_input_aggregate_fake_witness_data(&data);
709        assert_eq!(required_wits.vkeys.len(), 1);
710    }
711
712    #[test]
713    fn vkey_test() {
714        let mut builder = TransactionWitnessSetBuilder::new();
715        let raw_key_public = fake_raw_key_public(0);
716        let fake_sig = fake_raw_key_sig(0);
717
718        // add the same element twice
719        builder.add_vkey(Vkeywitness::new(raw_key_public.clone(), fake_sig.clone()));
720        builder.add_vkey(Vkeywitness::new(raw_key_public, fake_sig));
721
722        // add a different element
723        builder.add_vkey(Vkeywitness::new(
724            fake_raw_key_public(1),
725            fake_raw_key_sig(1),
726        ));
727
728        let wit_set = builder.build();
729        assert_eq!(wit_set.vkeywitnesses.unwrap().len(), 2);
730    }
731
732    #[test]
733    fn bootstrap_test() {
734        let mut builder = TransactionWitnessSetBuilder::new();
735
736        // add the same element twice
737        let wit1 = make_icarus_bootstrap_witness(
738            TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]),
739            ByronAddress::from_base58(
740                "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
741            )
742            .unwrap(),
743            &fake_private_key1(),
744        );
745        builder.add_bootstrap(wit1.clone());
746        builder.add_bootstrap(wit1);
747
748        // add a different element
749        builder.add_bootstrap(make_icarus_bootstrap_witness(
750            TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]),
751            ByronAddress::from_base58(
752                "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
753            )
754            .unwrap(),
755            &fake_private_key2(),
756        ));
757
758        let wit_set = builder.build();
759        assert_eq!(wit_set.bootstrap_witnesses.unwrap().len(), 2);
760    }
761
762    #[test]
763    fn native_script_test() {
764        let mut builder = TransactionWitnessSetBuilder::new();
765
766        // add the same element twice
767        let wit1: Script = NativeScript::new_script_invalid_before(1).into();
768        builder.add_script(wit1.clone());
769        builder.add_script(wit1);
770
771        // add a different element
772        builder.add_script(NativeScript::new_script_invalid_before(2).into());
773
774        let wit_set = builder.build();
775        assert_eq!(wit_set.native_scripts.unwrap().len(), 2);
776    }
777
778    // TODO: tests for plutus scripts (v1 & v2), plutus_data, redeemers
779    // once we have mock data for them
780    #[test]
781    fn requirement_test_fail() {
782        let mut builder = TransactionWitnessSetBuilder::new();
783
784        let mut required_wits = RequiredWitnessSet::new();
785        required_wits.add_vkey_key_hash(fake_raw_key_public(0).hash());
786        required_wits.add_script_hash(NativeScript::new_script_invalid_before(2).hash());
787        builder.add_required_wits(required_wits);
788
789        // add a different element
790        builder.add_vkey(Vkeywitness::new(
791            fake_raw_key_public(1),
792            fake_raw_key_sig(1),
793        ));
794
795        assert!(builder.try_build().is_err());
796    }
797
798    #[test]
799    fn requirement_test_pass() {
800        let mut builder = TransactionWitnessSetBuilder::new();
801
802        let mut required_wits = RequiredWitnessSet::new();
803        required_wits.add_vkey_key_hash(fake_raw_key_public(0).hash());
804        builder.add_required_wits(required_wits);
805
806        // add a different element
807        builder.add_vkey(Vkeywitness::new(
808            fake_raw_key_public(0),
809            fake_raw_key_sig(0),
810        ));
811
812        assert!(builder.try_build().is_ok());
813    }
814
815    #[test]
816    fn tx_witness_set_roundtrip_test() {
817        let data = "a102818458205e8379f58f0838234af67f73738f0fee0d8185232e200b8e42887f4f06544a9a5840f5cfea560d2f8645ed624b65bf08cf83346eb5168ee4df0f63ce2d0d5f677db88fef2d5d9f032f09223889b5e85504ab44dd0a0cde1f1fd8f57deefde8c2080658202d3b7d9b806f88f10f1193e94ef97e5c02370c1464f61a30a8f1ac1a46115b2d5829a201581e581c072931653330243cf126aea85d39e73c6bd04601fe77424efb9e371002451a4170cb17";
818        let witness_set =
819            TransactionWitnessSet::from_cbor_bytes(&hex::decode(data).unwrap()).unwrap();
820        let round_trip = witness_set.to_cbor_bytes();
821
822        assert_eq!(data, hex::encode(round_trip));
823    }
824}