Skip to main content

hns_script/
lib.rs

1#![doc = "Consensus-visible Handshake script and signature-hash primitives."]
2
3mod interpreter;
4
5pub use interpreter::*;
6
7use blake2::Blake2bVar;
8use blake2::digest::{Update, VariableOutput};
9use hns_encoding::Encoder;
10use hns_transaction::{Outpoint, Transaction};
11
12pub const SIGHASH_ALL: u32 = 1;
13pub const SIGHASH_NONE: u32 = 2;
14pub const SIGHASH_SINGLE: u32 = 3;
15pub const SIGHASH_SINGLE_REVERSE: u32 = 4;
16pub const SIGHASH_NOINPUT: u32 = 0x40;
17pub const SIGHASH_ANYONE_CAN_PAY: u32 = 0x80;
18pub const SIGHASH_BASE_MASK: u32 = 0x1f;
19pub const HIP1_SELLER_SIGHASH: u32 = SIGHASH_SINGLE_REVERSE | SIGHASH_ANYONE_CAN_PAY;
20
21pub const LOCKTIME_FLAG: u32 = 1 << 31;
22pub const LOCKTIME_MASK: u32 = LOCKTIME_FLAG - 1;
23pub const LOCKTIME_GRANULARITY: u32 = 9;
24pub const LOCKTIME_MULTIPLIER: u32 = 1 << LOCKTIME_GRANULARITY;
25pub const SEQUENCE_DISABLE_FLAG: u32 = 1 << 31;
26pub const SEQUENCE_TYPE_FLAG: u32 = 1 << 22;
27pub const SEQUENCE_GRANULARITY: u32 = 9;
28pub const SEQUENCE_MASK: u32 = 0x0000_ffff;
29
30pub const fn is_valid_signature_hash_type(hash_type: u8) -> bool {
31    let normalized = (hash_type as u32) & !(SIGHASH_NOINPUT | SIGHASH_ANYONE_CAN_PAY);
32    normalized >= SIGHASH_ALL && normalized <= SIGHASH_SINGLE_REVERSE
33}
34
35pub fn signature_hash(
36    transaction: &Transaction,
37    input_index: usize,
38    previous_script: &[u8],
39    previous_value: u64,
40    hash_type: u32,
41) -> Result<[u8; 32], ScriptError> {
42    let input = transaction
43        .inputs
44        .get(input_index)
45        .ok_or(ScriptError::InputIndex {
46            requested: input_index,
47            inputs: transaction.inputs.len(),
48        })?;
49    let base = hash_type & SIGHASH_BASE_MASK;
50    if !(SIGHASH_ALL..=SIGHASH_SINGLE_REVERSE).contains(&base) {
51        return Err(ScriptError::InvalidSignatureHashType(hash_type));
52    }
53    let anyone_can_pay = hash_type & SIGHASH_ANYONE_CAN_PAY != 0;
54    let no_input = hash_type & SIGHASH_NOINPUT != 0;
55    let zero_hash = [0_u8; 32];
56
57    let hash_prevouts = if anyone_can_pay {
58        zero_hash
59    } else {
60        let mut bytes = Vec::with_capacity(transaction.inputs.len().saturating_mul(36));
61        for transaction_input in &transaction.inputs {
62            bytes.extend_from_slice(&transaction_input.previous_output.encode());
63        }
64        blake2b_256(&bytes)
65    };
66
67    let hash_sequences = if anyone_can_pay
68        || matches!(base, SIGHASH_NONE | SIGHASH_SINGLE | SIGHASH_SINGLE_REVERSE)
69    {
70        zero_hash
71    } else {
72        let mut encoder = Encoder::with_capacity(transaction.inputs.len().saturating_mul(4));
73        for transaction_input in &transaction.inputs {
74            encoder.put_u32_le(transaction_input.sequence);
75        }
76        blake2b_256(&encoder.into_bytes())
77    };
78
79    let hash_outputs = match base {
80        SIGHASH_NONE => zero_hash,
81        SIGHASH_SINGLE => transaction
82            .outputs
83            .get(input_index)
84            .map(|output| output.encode().map(|bytes| blake2b_256(&bytes)))
85            .transpose()?
86            .unwrap_or(zero_hash),
87        SIGHASH_SINGLE_REVERSE => {
88            if input_index < transaction.outputs.len() {
89                let output_index = transaction.outputs.len() - 1 - input_index;
90                blake2b_256(&transaction.outputs[output_index].encode()?)
91            } else {
92                zero_hash
93            }
94        }
95        SIGHASH_ALL => {
96            let mut bytes = Vec::new();
97            for output in &transaction.outputs {
98                bytes.extend_from_slice(&output.encode()?);
99            }
100            blake2b_256(&bytes)
101        }
102        _ => unreachable!("signature hash base was checked"),
103    };
104
105    let (current_outpoint, current_sequence) = if no_input {
106        (Outpoint::NULL, u32::MAX)
107    } else {
108        (input.previous_output, input.sequence)
109    };
110
111    let mut encoder = Encoder::with_capacity(156_usize.saturating_add(previous_script.len()));
112    encoder.put_u32_le(transaction.version);
113    encoder.put_bytes(&hash_prevouts);
114    encoder.put_bytes(&hash_sequences);
115    encoder.put_bytes(&current_outpoint.encode());
116    encoder.put_varbytes(previous_script);
117    encoder.put_u64_le(previous_value);
118    encoder.put_u32_le(current_sequence);
119    encoder.put_bytes(&hash_outputs);
120    encoder.put_u32_le(transaction.locktime);
121    encoder.put_u32_le(hash_type);
122    Ok(blake2b_256(&encoder.into_bytes()))
123}
124
125pub fn verify_locktime_predicate(
126    transaction: &Transaction,
127    input_index: usize,
128    predicate: u32,
129) -> bool {
130    let Some(input) = transaction.inputs.get(input_index) else {
131        return false;
132    };
133    (transaction.locktime & LOCKTIME_FLAG) == (predicate & LOCKTIME_FLAG)
134        && (predicate & LOCKTIME_MASK) <= (transaction.locktime & LOCKTIME_MASK)
135        && input.sequence != u32::MAX
136}
137
138pub fn verify_sequence_predicate(
139    transaction: &Transaction,
140    input_index: usize,
141    predicate: u32,
142) -> bool {
143    let Some(input) = transaction.inputs.get(input_index) else {
144        return false;
145    };
146    if predicate & SEQUENCE_DISABLE_FLAG != 0 {
147        return true;
148    }
149    if input.sequence & SEQUENCE_DISABLE_FLAG != 0 {
150        return false;
151    }
152    (input.sequence & SEQUENCE_TYPE_FLAG) == (predicate & SEQUENCE_TYPE_FLAG)
153        && (predicate & SEQUENCE_MASK) <= (input.sequence & SEQUENCE_MASK)
154}
155
156fn blake2b_256(input: &[u8]) -> [u8; 32] {
157    let mut hasher = Blake2bVar::new(32).expect("valid BLAKE2b output length");
158    hasher.update(input);
159    let mut output = [0_u8; 32];
160    hasher
161        .finalize_variable(&mut output)
162        .expect("valid BLAKE2b output buffer");
163    output
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169
170    const RAW: &str = "020000000211111111111111111111111111111111111111111111111111111111111111110300000078563412222222222222222222222222222222222222222222222222222222222222222205000000214365870307b20100000000000014aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa00000e640300000000000020bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb000015160500000000000014cccccccccccccccccccccccccccccccccccccccc0000907856340000";
171
172    #[test]
173    fn signature_hashes_match_pinned_hsd_oracle() {
174        let transaction =
175            Transaction::decode(&hex::decode(RAW).expect("hex")).expect("transaction");
176        let script = hex::decode("5253935587").expect("hex");
177        let vectors = [
178            (
179                0,
180                1,
181                "cb4a35c13f76461bb3643ce11b1fc67b2b714abf0c7f6e1e2e7047068f35b48c",
182            ),
183            (
184                0,
185                2,
186                "e8b7106ef2f0df452b12364631d378e2d0e8d038788c1b3d26a101303544293e",
187            ),
188            (
189                0,
190                3,
191                "2badccb2547452294fce40200dde2e0b815f533736b0501d92241ec3877f953a",
192            ),
193            (
194                0,
195                4,
196                "36635027506339b57147797c1c21896f67885701c6d60852aa5e7d6a11957b9d",
197            ),
198            (
199                0,
200                65,
201                "3308248a050d86b124892b19b7ebf11a45d0c8f7068859de6e23e42758fa0ab4",
202            ),
203            (
204                0,
205                68,
206                "f10465c8399650d0be57443cdbf120361abd3ad23ccee6d53fc527d96a457e82",
207            ),
208            (
209                0,
210                129,
211                "e701f42f9acdbae7701c7e9385920c3703464ef1a0a470614f4d4050363d82dd",
212            ),
213            (
214                0,
215                132,
216                "ad3258f7941426d7fdb0156d8aa54e9d5be1c5e92835acfac26b8ad64d0be412",
217            ),
218            (
219                0,
220                193,
221                "ab225099577a95638bce984856b66fd120841ca973e8ac28326c30fefeaecbf7",
222            ),
223            (
224                0,
225                196,
226                "bdd91d3743dee83ac581820261735071babf6931fb411e1789287053c69bb65b",
227            ),
228            (
229                1,
230                1,
231                "4a4117ac47acdd10d82f0f05fb6899d015b5aa42cef22d224ce826ed8e33b3a2",
232            ),
233            (
234                1,
235                2,
236                "08c99aa330db462ac2157149e5f04be29c6d62119ba61249caaa1c4d23554701",
237            ),
238            (
239                1,
240                3,
241                "664feef71a0d2e5c9c8fe86ca390cd273cfe122e89457461f5832817eac0e912",
242            ),
243            (
244                1,
245                4,
246                "bf35522c50c16e473574695a6ddc1ecaf85ce382478faacf746e8ae504a75294",
247            ),
248            (
249                1,
250                65,
251                "3308248a050d86b124892b19b7ebf11a45d0c8f7068859de6e23e42758fa0ab4",
252            ),
253            (
254                1,
255                68,
256                "2b2b3d03745b2c8d2865585f21ec5952fbaca033366407c45c18467fd6779c7f",
257            ),
258            (
259                1,
260                129,
261                "8015e96e71335685c73f7ab7f435adc1d52d68fb85bc1ca12fd65c8f7c614799",
262            ),
263            (
264                1,
265                132,
266                "a515e6e7fcfb29dac6d22da2916c11f0b078414820e63333357419589bbdca3a",
267            ),
268            (
269                1,
270                193,
271                "ab225099577a95638bce984856b66fd120841ca973e8ac28326c30fefeaecbf7",
272            ),
273            (
274                1,
275                196,
276                "e26f14476b55a26a0999e4f1b196fc718aa37b56d7db3ea70007617c59f23018",
277            ),
278        ];
279        for (input_index, hash_type, expected) in vectors {
280            assert_eq!(
281                hex::encode(
282                    signature_hash(&transaction, input_index, &script, 987_654_321, hash_type,)
283                        .expect("sighash"),
284                ),
285                expected,
286                "input {input_index} type {hash_type:#x}",
287            );
288        }
289    }
290
291    #[test]
292    fn hash_type_and_lock_predicates_match_hsd_boundaries() {
293        for base in 1_u8..=4 {
294            assert!(is_valid_signature_hash_type(base));
295            assert!(is_valid_signature_hash_type(base | SIGHASH_NOINPUT as u8));
296            assert!(is_valid_signature_hash_type(
297                base | SIGHASH_ANYONE_CAN_PAY as u8
298            ));
299        }
300        assert!(!is_valid_signature_hash_type(0));
301        assert!(!is_valid_signature_hash_type(5));
302
303        let mut transaction =
304            Transaction::decode(&hex::decode(RAW).expect("hex")).expect("transaction");
305        transaction.locktime = 10;
306        transaction.inputs[0].sequence = 7;
307        assert!(verify_locktime_predicate(&transaction, 0, 9));
308        assert!(!verify_locktime_predicate(&transaction, 0, 11));
309        assert!(verify_sequence_predicate(&transaction, 0, 6));
310        assert!(!verify_sequence_predicate(&transaction, 0, 8));
311    }
312}