bitsplain/
types.rs

1//! Basic and common data types and their parsers.
2
3use bitcoin::blockdata::constants::genesis_block;
4use bitcoin::hashes::{sha256d, Hash};
5use bitcoin::secp256k1::ecdsa::Signature;
6use bitcoin::{BlockHash, Network, PublicKey, Txid};
7use nom::combinator::success;
8use nom::multi::length_count;
9use nom::number::streaming::*;
10use nom::{Parser, ToUsize};
11use rust_decimal::prelude::*;
12use time::OffsetDateTime;
13
14use crate::parse::*;
15use crate::value::*;
16use crate::*;
17
18#[derive(Clone, Copy, Debug)]
19pub struct Sat(Decimal);
20
21const SATS: Decimal = Decimal::from_parts(100000000, 0, 0, false, 0);
22
23impl Sat {
24    pub fn new(sat: u64) -> Sat {
25        Sat(Decimal::from_u64(sat).unwrap())
26    }
27
28    pub fn sat(&self) -> u64 {
29        self.0.to_u64().unwrap()
30    }
31
32    pub fn btc(&self) -> Decimal {
33        self.0 / SATS
34    }
35
36    pub fn as_str(&self) -> String {
37        format!("{} ₿", self.btc())
38    }
39}
40
41pub fn sat(input: Span) -> Parsed<Sat> {
42    with("datatype", "sat", le_u64)(input).map(|(s, n)| (s, Sat::new(n)))
43}
44
45/// Internal representation of chain hash.
46#[derive(Clone, Debug)]
47pub struct ChainHash {
48    pub block_hash: BlockHash,
49    pub network: Option<Network>,
50}
51
52impl ChainHash {
53    pub fn as_string(&self) -> String {
54        match self.network {
55            Some(n) => n.to_string(),
56            None => "unknown".to_string(),
57        }
58    }
59}
60
61impl ToValue for ChainHash {
62    fn to_value(&self) -> Value {
63        Value::Alt(
64            Box::new(Value::Hash(self.block_hash.as_hash())),
65            Box::new(Value::text(self.as_string())),
66        )
67    }
68}
69
70/// Parser of chain hash, little endian.
71pub fn chain_hash_le(s: Span) -> Parsed<ChainHash> {
72    let (s, mut b) = bytes(32_usize)(s)?;
73
74    b.reverse();
75
76    let block_hash = BlockHash::from_slice(&b).unwrap();
77
78    let network = if block_hash == genesis_block(Network::Bitcoin).block_hash() {
79        Some(Network::Bitcoin)
80    } else if block_hash == genesis_block(Network::Regtest).block_hash() {
81        Some(Network::Regtest)
82    } else if block_hash == genesis_block(Network::Testnet).block_hash() {
83        Some(Network::Testnet)
84    } else if block_hash == genesis_block(Network::Signet).block_hash() {
85        Some(Network::Signet)
86    } else {
87        None
88    };
89
90    Ok((
91        s.with("datatype", "chain_hash"),
92        ChainHash {
93            block_hash,
94            network,
95        },
96    ))
97}
98
99/// Parser of chain hash, big endian.
100pub fn chain_hash_be(s: Span) -> Parsed<ChainHash> {
101    let (s, b) = bytes(32_usize)(s)?;
102
103    let block_hash = BlockHash::from_slice(&b).unwrap();
104
105    let network = if block_hash == genesis_block(Network::Bitcoin).block_hash() {
106        Some(Network::Bitcoin)
107    } else if block_hash == genesis_block(Network::Regtest).block_hash() {
108        Some(Network::Regtest)
109    } else if block_hash == genesis_block(Network::Testnet).block_hash() {
110        Some(Network::Testnet)
111    } else if block_hash == genesis_block(Network::Signet).block_hash() {
112        Some(Network::Signet)
113    } else {
114        None
115    };
116
117    Ok((
118        s.with("datatype", "chain_hash"),
119        ChainHash {
120            block_hash,
121            network,
122        },
123    ))
124}
125
126pub fn bytes<'a, U: ToUsize + std::fmt::Debug + Copy>(
127    len: U,
128) -> impl Fn(Span<'a>) -> Parsed<'a, Vec<u8>> {
129    move |input: Span<'a>| with("datatype", "bytes", length_count(success(len), u8))(input)
130}
131
132pub fn sha256d(input: Span) -> Parsed<sha256d::Hash> {
133    let (s, x) = bytes(32_usize)(input)?;
134    let x = sha256d::Hash::from_slice(&x).unwrap();
135    Ok((s.with("datatype", "sha256"), x))
136}
137
138pub fn txid(input: Span) -> Parsed<Txid> {
139    let (s, x) = sha256d(input)?;
140    Ok((s.with("datatype", "txid"), Txid::from_hash(x)))
141}
142
143pub fn signature(input: Span) -> Parsed<Signature> {
144    let (s, b) = bytes(64_usize)(input)?;
145    match Signature::from_compact(&b) {
146        Ok(sig) => Ok((s.with("datatype", "signature"), sig)),
147        Err(_) => Err(nom::Err::Failure(nom::error::Error {
148            input: s,
149            code: nom::error::ErrorKind::Fail,
150        })),
151    }
152}
153
154pub fn public_key(input: Span) -> Parsed<PublicKey> {
155    let (s, b) = bytes(33_usize)(input)?;
156    match PublicKey::from_slice(&b) {
157        Ok(pk) => Ok((s.with("datatype", "public_key"), pk)),
158        Err(_) => Err(nom::Err::Failure(nom::error::Error {
159            input: s,
160            code: nom::error::ErrorKind::Fail,
161        })),
162    }
163}
164
165fn varint_impl(input: Span) -> Parsed<u64> {
166    let (s, byte) = le_u8(input)?;
167
168    let s_int = match byte {
169        0xfd => {
170            let (s, a) = le_u16(s)?;
171            (s, a as u64)
172        }
173        0xfe => {
174            let (s, a) = le_u32(s)?;
175            (s, a as u64)
176        }
177        0xff => le_u64(s)?,
178        n => success(n as u64)(s)?,
179    };
180    Ok(s_int)
181}
182
183pub fn uint32(input: Span) -> Parsed<u32> {
184    with("datatype", "uint32", le_u32)(input)
185}
186
187pub fn int32(input: Span) -> Parsed<i32> {
188    with("datatype", "int32", le_i32)(input)
189}
190
191pub fn varint(input: Span) -> Parsed<u64> {
192    with("datatype", "varint", varint_impl)(input)
193}
194
195/// Unix timestamp parser. Provided parser is used for the numeric value,
196/// typically `uint32` or `be_u32`.
197pub fn timestamp<'a, Parse>(mut parser: Parse) -> impl FnMut(Span<'a>) -> Parsed<OffsetDateTime>
198where
199    Parse: Parser<Span<'a>, u32, nom::error::Error<Span<'a>>>,
200{
201    move |input: Span| {
202        parser.parse(input).map(|(s, ts)| {
203            (
204                s.with("datatype", "timestamp"),
205                OffsetDateTime::from_unix_timestamp(ts.into()).unwrap(),
206            )
207        })
208    }
209}