bitcoin_internals/
script.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Internal script related helper functions and types.
4
5/// Reads a `usize` from an iterator.
6///
7/// A script push data instruction includes the length of the data being pushed, this function reads
8/// that length from an iterator (encoded in either 1, 2, or 4 bytes).
9///
10/// # Errors
11///
12/// Returns an error if the iterator does not contain enough bytes to read the length.
13// We internally use implementation based on iterator so that it automatically advances as needed.
14pub fn read_push_data_len(
15    data: &mut core::slice::Iter<'_, u8>,
16    size: PushDataLenLen,
17) -> Result<usize, EarlyEndOfScriptError> {
18    // The `size` enum enforces that the maximum shift will be 32 and
19    // that we can only ever read up to 4 bytes.
20    let size = size as usize;
21
22    if data.len() < size {
23        return Err(EarlyEndOfScriptError);
24    };
25
26    let mut ret = 0;
27    for (i, item) in data.take(size).enumerate() {
28        ret |= usize::from(*item) << (i * 8);
29    }
30    Ok(ret)
31}
32
33/// The number of bytes used to encode an unsigned integer as the length of a push data instruction.
34///
35/// This makes it easier to prove correctness of `next_push_data_len` and `read_push_data_len`.
36#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
37pub enum PushDataLenLen {
38    /// Unsigned integer comprising of a single byte.
39    One = 1,
40    /// Unsigned integer comprising of two bytes.
41    Two = 2,
42    /// Unsigned integer comprising of four bytes.
43    Four = 4,
44}
45
46/// Indicates that we tried to read more bytes from the script than available.
47#[derive(Debug)]
48pub struct EarlyEndOfScriptError;
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn reads_4_bytes() {
56        let bytes = [0x01, 0x23, 0x45, 0x67];
57        let want = u32::from_le_bytes([0x01, 0x23, 0x45, 0x67]);
58        let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::Four).unwrap();
59        assert_eq!(got, want as usize);
60    }
61
62    #[test]
63    fn reads_2_bytes() {
64        let bytes = [0x01, 0x23];
65        let want = u16::from_le_bytes([0x01, 0x23]);
66        let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::Two).unwrap();
67        assert_eq!(got, want as usize);
68    }
69
70    #[test]
71    fn reads_1_byte() {
72        let bytes = [0x01];
73        let want = 0x01;
74        let got = read_push_data_len(&mut bytes.iter(), PushDataLenLen::One).unwrap();
75        assert_eq!(got, want as usize);
76    }
77}