1#![warn(missing_docs)]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4#[doc(hidden)]
19#[macro_use]
20pub mod macros;
21
22pub mod btcspv;
26
27pub mod validatespv;
30
31pub mod types;
33
34#[cfg(feature = "std")]
38pub mod std_types;
39
40#[cfg(feature = "std")]
43pub mod utils;
44
45#[cfg(test)]
46#[doc(hidden)]
47#[cfg_attr(tarpaulin, skip)]
48pub mod test_utils {
49
50 extern crate hex;
51 extern crate std;
52
53 use primitive_types::U256;
54 use serde::Deserialize;
55
56 use crate::btcspv;
57 use crate::types::{RawHeader, SPVError};
58
59 use std::{
60 format,
61 fs::File,
62 io::Read,
63 panic,
64 string::String,
65 vec, vec::Vec, };
68
69 pub fn reverse_endianness(b: &[u8]) -> Vec<u8> {
76 b.iter().rev().copied().collect()
77 }
78
79 pub fn strip_0x_prefix(s: &str) -> &str {
85 if &s[..2] == "0x" {
86 &s[2..]
87 } else {
88 s
89 }
90 }
91
92 pub fn deserialize_hex(s: &str) -> Result<Vec<u8>, hex::FromHexError> {
98 hex::decode(&strip_0x_prefix(s))
99 }
100
101 pub fn serialize_hex(buf: &[u8]) -> String {
107 format!("0x{}", hex::encode(buf))
108 }
109
110 pub fn force_deserialize_hex(s: &str) -> Vec<u8> {
121 deserialize_hex(s).unwrap()
122 }
123
124 #[derive(Deserialize, Debug)]
125 pub struct TestCase {
126 pub input: serde_json::Value,
127 pub output: serde_json::Value,
128 pub error_message: serde_json::Value,
129 }
130
131 pub struct TestHeader {
132 pub raw: RawHeader,
133 pub timestamp: u32,
134 pub target: U256,
135 pub difficulty: U256,
136 }
137
138 pub fn to_test_header(head: &serde_json::map::Map<String, serde_json::Value>) -> TestHeader {
139 let mut raw = RawHeader::default();
140 raw.as_mut().copy_from_slice(&force_deserialize_hex(
141 head.get("hex").unwrap().as_str().unwrap(),
142 ));
143
144 let timestamp = head.get("timestamp").unwrap().as_u64().unwrap() as u32;
145 let target = btcspv::extract_target(raw);
146 let difficulty = btcspv::calculate_difficulty(&target);
147 TestHeader {
148 raw,
149 timestamp,
150 target,
151 difficulty,
152 }
153 }
154
155 pub fn get_headers(heads: &serde_json::Value) -> Vec<TestHeader> {
156 let vals: &Vec<serde_json::Value> = heads.as_array().unwrap();
157 let mut headers = vec![];
158 for i in vals {
159 headers.push(to_test_header(&i.as_object().unwrap()));
160 }
161 headers
162 }
163
164 pub fn setup() -> serde_json::Value {
165 let mut file = File::open("../testVectors.json").unwrap();
166 let mut data = String::new();
167 file.read_to_string(&mut data).unwrap();
168
169 serde_json::from_str(&data).unwrap()
170 }
171
172 pub fn to_test_case(val: &serde_json::Value) -> TestCase {
173 let o = val.get("output");
174 let output: &serde_json::Value;
175 output = match o {
176 Some(v) => v,
177 None => &serde_json::Value::Null,
178 };
179
180 let e = val.get("rustError");
181 let error_message: &serde_json::Value;
182 error_message = match e {
183 Some(v) => v,
184 None => &serde_json::Value::Null,
185 };
186
187 TestCase {
188 input: val.get("input").unwrap().clone(),
189 output: output.clone(),
190 error_message: error_message.clone(),
191 }
192 }
193
194 pub fn get_test_cases(name: &str, fixtures: &serde_json::Value) -> Vec<TestCase> {
195 let vals: &Vec<serde_json::Value> = fixtures.get(name).unwrap().as_array().unwrap();
196 let mut cases = vec![];
197 for i in vals {
198 cases.push(to_test_case(&i));
199 }
200 cases
201 }
202
203 pub fn match_string_to_err(s: &str) -> SPVError {
204 match s {
205 "Malformatted data. Read overrun" => SPVError::ReadOverrun,
206 "Read overrun" => SPVError::ReadOverrun,
207 "Vout read overrun" => SPVError::ReadOverrun,
208 "Vin read overrun" => SPVError::ReadOverrun,
209 "Read overrun when parsing vout" => SPVError::ReadOverrun,
210 "Read overrun when parsing vin" => SPVError::ReadOverrun,
211 "Bad VarInt in scriptPubkey" => SPVError::BadCompactInt,
212 "Bad VarInt in scriptSig" => SPVError::BadCompactInt,
213 "Read overrun during VarInt parsing" => SPVError::BadCompactInt,
214 "Malformatted data. Must be an op return" => SPVError::MalformattedOpReturnOutput,
215 "Maliciously formatted p2sh output" => SPVError::MalformattedP2SHOutput,
216 "Maliciously formatted p2pkh output" => SPVError::MalformattedP2PKHOutput,
217 "Maliciously formatted witness output" => SPVError::MalformattedWitnessOutput,
218 "Nonstandard, OP_RETURN, or malformatted output" => SPVError::MalformattedOutput,
219 "Header bytes not multiple of 80" => SPVError::WrongLengthHeader,
220 "Header does not meet its own difficulty target" => SPVError::InsufficientWork,
221 "Header bytes not a valid chain" => SPVError::InvalidChain,
222 "Hash is not the correct hash of the header" => SPVError::WrongDigest,
223 "MerkleRoot is not the correct merkle root of the header" => SPVError::WrongMerkleRoot,
224 "Prevhash is not the correct parent hash of the header" => SPVError::WrongPrevHash,
225 "Vin is not valid" => SPVError::InvalidVin,
226 "Vout is not valid" => SPVError::InvalidVout,
227 "Version, Vin, Vout and Locktime did not yield correct TxID" => SPVError::WrongTxID,
228 "Merkle Proof is not valid" => SPVError::BadMerkleProof,
229 "Reported length mismatch" => SPVError::OutputLengthMismatch,
230 _ => SPVError::UnknownError,
231 }
232 }
233
234 pub fn run_test<T>(test: T)
235 where
236 T: FnOnce(&serde_json::Value) -> () + panic::UnwindSafe,
237 {
238 let fixtures = setup();
239
240 let result = panic::catch_unwind(|| test(&fixtures));
241
242 assert!(result.is_ok())
243 }
244
245 #[test]
246 fn it_strips_0x_prefixes() {
247 let cases = [
248 ("00", "00"),
249 ("0x00", "00"),
250 ("aa", "aa"),
251 ("0xaa", "aa"),
252 ("Quotidian", "Quotidian"),
253 ("0xQuotidian", "Quotidian"),
254 ];
255 for case in cases.iter() {
256 assert_eq!(strip_0x_prefix(case.0), case.1);
257 }
258 }
259}