1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Miniscript
// Written in 2021 by
//     Andrew Poelstra <apoelstra@wpsoftware.net>
//     Sanket Kanjalkar <sanket1729@gmail.com>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! Covenant Descriptor Satisfaction

use elements::encode::Encodable;
use elements::hashes::{sha256d, Hash};
use elements::sighash::SigHashCache;
use elements::{self, confidential, EcdsaSigHashType, OutPoint, Script, SigHash, Transaction};

use super::CovError;
use crate::{MiniscriptKey, Satisfier, ToPublicKey};

/// A satisfier for Covenant descriptors
/// that can do transaction introspection
/// 'tx denotes the lifetime of the transaction
/// being satisfied and 'ptx denotes the lifetime
/// of the previous transaction inputs
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LegacyCovSatisfier<'tx, 'ptx> {
    // Common fields in Segwit and Taphash
    /// The transaction being spent
    tx: &'tx Transaction,
    /// The script code required for
    /// The input index being spent
    idx: u32,
    /// The sighash type
    hash_type: EcdsaSigHashType,

    // Segwitv0
    /// The script code required for segwit sighash
    script_code: Option<&'ptx Script>,
    /// The value of the output being spent
    value: Option<confidential::Value>,
}

impl<'tx, 'ptx> LegacyCovSatisfier<'tx, 'ptx> {
    /// Create  a new Covsatisfier for v0 spends
    /// Panics if idx is out of bounds
    pub fn new_segwitv0(
        tx: &'tx Transaction,
        idx: u32,
        value: confidential::Value,
        script_code: &'ptx Script,
        hash_type: EcdsaSigHashType,
    ) -> Self {
        assert!((idx as usize) < tx.input.len());
        Self {
            tx,
            idx,
            hash_type,
            script_code: Some(script_code),
            value: Some(value),
        }
    }

    /// Easy way to get sighash since we already have
    /// all the required information.
    /// Note that this does not do any caching, so it
    /// will be slightly inefficient as compared to
    /// using sighash
    pub fn segwit_sighash(&self) -> Result<SigHash, CovError> {
        let mut cache = SigHashCache::new(self.tx);
        // TODO: error types
        let script_code = self.script_code.ok_or(CovError::MissingScriptCode)?;
        let value = self.value.ok_or(CovError::MissingValue)?;
        Ok(cache.segwitv0_sighash(self.idx as usize, script_code, value, self.hash_type))
    }
}

impl<'tx, 'ptx, Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for LegacyCovSatisfier<'tx, 'ptx> {
    fn lookup_nversion(&self) -> Option<u32> {
        Some(self.tx.version)
    }

    fn lookup_hashprevouts(&self) -> Option<sha256d::Hash> {
        let mut enc = sha256d::Hash::engine();
        for txin in &self.tx.input {
            txin.previous_output.consensus_encode(&mut enc).unwrap();
        }
        Some(sha256d::Hash::from_engine(enc))
    }

    fn lookup_hashsequence(&self) -> Option<sha256d::Hash> {
        let mut enc = sha256d::Hash::engine();
        for txin in &self.tx.input {
            txin.sequence.consensus_encode(&mut enc).unwrap();
        }
        Some(sha256d::Hash::from_engine(enc))
    }

    fn lookup_hashissuances(&self) -> Option<sha256d::Hash> {
        let mut enc = sha256d::Hash::engine();
        for txin in &self.tx.input {
            if txin.has_issuance() {
                txin.asset_issuance.consensus_encode(&mut enc).unwrap();
            } else {
                0u8.consensus_encode(&mut enc).unwrap();
            }
        }
        Some(sha256d::Hash::from_engine(enc))
    }

    fn lookup_outpoint(&self) -> Option<OutPoint> {
        Some(self.tx.input[self.idx as usize].previous_output)
    }

    fn lookup_scriptcode(&self) -> Option<&Script> {
        self.script_code
    }

    fn lookup_value(&self) -> Option<confidential::Value> {
        self.value
    }

    fn lookup_nsequence(&self) -> Option<u32> {
        Some(self.tx.input[self.idx as usize].sequence.to_consensus_u32())
    }

    fn lookup_outputs(&self) -> Option<&[elements::TxOut]> {
        Some(&self.tx.output)
    }

    fn lookup_nlocktime(&self) -> Option<u32> {
        Some(self.tx.lock_time.to_consensus_u32())
    }

    fn lookup_sighashu32(&self) -> Option<u32> {
        Some(self.hash_type.as_u32())
    }
}