Skip to main content

hns_script/
lib.rs

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