floresta_common/
lib.rs

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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// SPDX-License-Identifier: MIT OR Apache-2.0

//! # Floresta Common
//! Provides utility functions, macros and modules to be
//! used in other Floresta crates.

// cargo docs customization
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/249173822")]
#![doc(
    html_favicon_url = "https://raw.githubusercontent.com/getfloresta/floresta-media/master/logo_png/Icon-Green(main).png"
)]
#![no_std]

use bitcoin::consensus::encode;
use bitcoin::consensus::Decodable;
use bitcoin::hashes::sha256;
use bitcoin::hashes::Hash;
use bitcoin::ScriptBuf;
use bitcoin::VarInt;
#[cfg(any(feature = "descriptors-std", feature = "descriptors-no-std"))]
use miniscript::Descriptor;
#[cfg(any(feature = "descriptors-std", feature = "descriptors-no-std"))]
use miniscript::DescriptorPublicKey;
use sha2::Digest;

#[cfg(feature = "std")]
mod ema;
pub mod macros;
pub mod spsc;

#[cfg(feature = "std")]
pub use ema::Ema;
#[cfg(any(feature = "descriptors-std", feature = "descriptors-no-std"))]
use prelude::*;
pub use spsc::Channel;

/// Computes the SHA-256 digest of the byte slice data and returns a [Hash] from `bitcoin_hashes`.
///
/// [Hash]: https://docs.rs/bitcoin_hashes/latest/bitcoin_hashes/sha256/struct.Hash.html
pub fn get_hash_from_u8(data: &[u8]) -> sha256::Hash {
    let hash = sha2::Sha256::new().chain_update(data).finalize();
    sha256::Hash::from_byte_array(hash.into())
}

/// Computes the SHA-256 digest of a script, reverses its bytes, and returns a [Hash] from
/// `bitcoin_hashes`.
///
/// The source to the specification can be found in the Electrum protocol [documentation], and it is
/// used to identify scripts in the Electrum Protocol.
///
/// [documentation]: https://electrum-protocol.readthedocs.io/en/latest/protocol-basics.html#script-hashes
/// [Hash]: https://docs.rs/bitcoin_hashes/latest/bitcoin_hashes/sha256/struct.Hash.html
pub fn get_spk_hash(spk: &ScriptBuf) -> sha256::Hash {
    let data = spk.as_bytes();
    let mut hash = sha2::Sha256::new().chain_update(data).finalize();
    hash.reverse();
    sha256::Hash::from_byte_array(hash.into())
}

/// Reads a VarInt from the given reader and ensures it is less than or equal to `max`.
///
/// Returns an error if the VarInt is larger than `max`.
pub fn read_bounded_len<R: bitcoin::io::Read + ?Sized>(
    reader: &mut R,
    max: usize,
) -> Result<usize, encode::Error> {
    let n64 = VarInt::consensus_decode(reader)?.0;
    if n64 > max as u64 {
        return Err(encode::Error::OversizedVectorAllocation {
            requested: n64 as usize,
            max,
        });
    }
    Ok(n64 as usize)
}

/// Utreexo-specific service flags.
///
/// TODO(@luisschwab): remove this once <https://github.com/rust-bitcoin/rust-bitcoin/pull/5009> is merged.
pub mod service_flags {
    /// `UTREEXO`: the node is capable of serving inclusion proofs for new
    /// blocks and transactions, and for their other advertised services.
    pub const UTREEXO: u64 = 1 << 12;

    /// `UTREEXO_ARCHIVE`: the node is capable of serving historical
    /// inclusion proofs for all blocks, but not necessarily historical blocks.
    pub const UTREEXO_ARCHIVE: u64 = 1 << 13;
}

#[cfg(any(feature = "descriptors-std", feature = "descriptors-no-std"))]
/// Takes an array of descriptors as `String`, performs sanity checks on each one
/// and returns list of parsed descriptors.
pub fn parse_descriptors(
    descriptors: &[String],
) -> Result<Vec<Descriptor<DescriptorPublicKey>>, miniscript::Error> {
    use core::str::FromStr;

    let descriptors = descriptors
        .iter()
        .map(|descriptor| {
            let descriptor = Descriptor::<DescriptorPublicKey>::from_str(descriptor.as_str())?;
            descriptor.sanity_check()?;
            descriptor.into_single_descriptors()
        })
        .collect::<Result<Vec<Vec<_>>, _>>()?
        .into_iter()
        .flatten()
        .collect::<Vec<_>>();
    Ok(descriptors)
}

#[cfg(not(feature = "std"))]
pub mod prelude {
    extern crate alloc;
    pub use alloc::borrow::ToOwned;
    pub use alloc::boxed::Box;
    pub use alloc::format;
    pub use alloc::string::String;
    pub use alloc::string::ToString;
    pub use alloc::vec;
    pub use alloc::vec::Vec;
    pub use core::cmp;
    pub use core::convert;
    pub use core::iter;
    pub use core::mem;
    pub use core::ops;
    pub use core::ops::Deref;
    pub use core::ops::DerefMut;
    pub use core::option;
    pub use core::result;
    pub use core::slice;

    pub use bitcoin::io::Error as ioError;
    pub use bitcoin::io::Read;
    pub use bitcoin::io::Write;
    pub use hashbrown::HashMap;
    pub use hashbrown::HashSet;
}

#[cfg(feature = "std")]
/// Provides implementation for basic `std` types, without assuming we have a `std` library.
///
/// This module is used to avoid having `#[cfg(feature = "no-std")]` sprinkled
/// around all crates that support `no-std`. It imports all types we would use
/// from the `stdlib`, either from the lib itself, or from other sources in case
/// `stdlib` isn't available.
pub mod prelude {
    extern crate alloc;
    extern crate std;
    pub use alloc::format;
    pub use alloc::string::ToString;
    pub use std::borrow::ToOwned;
    pub use std::boxed::Box;
    pub use std::collections::hash_map::Entry;
    pub use std::collections::HashMap;
    pub use std::collections::HashSet;
    pub use std::io::Error as ioError;
    pub use std::io::Read;
    pub use std::io::Write;
    pub use std::ops::Deref;
    pub use std::ops::DerefMut;
    pub use std::result::Result;
    pub use std::string::String;
    pub use std::sync;
    pub use std::vec;
    pub use std::vec::Vec;
}

#[cfg(test)]
mod tests {
    use bitcoin::hashes::Hash;
    use bitcoin::hex::DisplayHex;
    use bitcoin::ScriptBuf;

    use super::prelude::*;

    #[test]
    fn test_get_hash_from_u8() {
        let data = b"Hello, world!";
        let hash = super::get_hash_from_u8(data);
        let expected =
            String::from("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3");
        assert_eq!(hash.as_byte_array().to_lower_hex_string(), expected);
    }

    #[test]
    fn test_get_spk_hash() {
        // Example taken from Electrum protocol documentation
        // https://electrum-protocol.readthedocs.io/en/latest/protocol-basics.html#script-hashes

        let spk =
            ScriptBuf::from_hex("76a91462e907b15cbf27d5425399ebf6f0fb50ebb88f1888ac").unwrap(); // P2PKH script
        let hash = super::get_spk_hash(&spk);
        let expected =
            String::from("8b01df4e368ea28f8dc0423bcf7a4923e3a12d307c875e47a0cfbf90b5c39161");

        assert_eq!(hash.as_byte_array().to_lower_hex_string(), expected);
    }
}