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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
#[doc(hidden)]
#[macro_use]
pub mod macros;
pub mod btcspv;
pub mod validatespv;
pub mod types;
#[cfg(feature = "std")]
pub mod std_types;
#[cfg(feature = "std")]
pub mod utils;
#[cfg(test)]
#[doc(hidden)]
#[cfg_attr(tarpaulin, skip)]
pub mod test_utils {
extern crate hex;
extern crate std;
use primitive_types::U256;
use serde::Deserialize;
use crate::btcspv;
use crate::types::{RawHeader, SPVError};
use std::{
format,
fs::File,
io::Read,
panic,
string::String,
vec,
vec::Vec,
};
pub fn reverse_endianness(b: &[u8]) -> Vec<u8> {
b.iter().rev().copied().collect()
}
pub fn strip_0x_prefix(s: &str) -> &str {
if &s[..2] == "0x" {
&s[2..]
} else {
s
}
}
pub fn deserialize_hex(s: &str) -> Result<Vec<u8>, hex::FromHexError> {
hex::decode(&strip_0x_prefix(s))
}
pub fn serialize_hex(buf: &[u8]) -> String {
format!("0x{}", hex::encode(buf))
}
pub fn force_deserialize_hex(s: &str) -> Vec<u8> {
deserialize_hex(s).unwrap()
}
#[derive(Deserialize, Debug)]
pub struct TestCase {
pub input: serde_json::Value,
pub output: serde_json::Value,
pub error_message: serde_json::Value,
}
pub struct TestHeader {
pub raw: RawHeader,
pub timestamp: u32,
pub target: U256,
pub difficulty: U256,
}
pub fn to_test_header(head: &serde_json::map::Map<String, serde_json::Value>) -> TestHeader {
let mut raw = RawHeader::default();
raw.as_mut().copy_from_slice(&force_deserialize_hex(
head.get("hex").unwrap().as_str().unwrap(),
));
let timestamp = head.get("timestamp").unwrap().as_u64().unwrap() as u32;
let target = btcspv::extract_target(raw);
let difficulty = btcspv::calculate_difficulty(&target);
TestHeader {
raw,
timestamp,
target,
difficulty,
}
}
pub fn get_headers(heads: &serde_json::Value) -> Vec<TestHeader> {
let vals: &Vec<serde_json::Value> = heads.as_array().unwrap();
let mut headers = vec![];
for i in vals {
headers.push(to_test_header(&i.as_object().unwrap()));
}
headers
}
pub fn setup() -> serde_json::Value {
let mut file = File::open("../testVectors.json").unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
serde_json::from_str(&data).unwrap()
}
pub fn to_test_case(val: &serde_json::Value) -> TestCase {
let o = val.get("output");
let output: &serde_json::Value;
output = match o {
Some(v) => v,
None => &serde_json::Value::Null,
};
let e = val.get("rustError");
let error_message: &serde_json::Value;
error_message = match e {
Some(v) => v,
None => &serde_json::Value::Null,
};
TestCase {
input: val.get("input").unwrap().clone(),
output: output.clone(),
error_message: error_message.clone(),
}
}
pub fn get_test_cases(name: &str, fixtures: &serde_json::Value) -> Vec<TestCase> {
let vals: &Vec<serde_json::Value> = fixtures.get(name).unwrap().as_array().unwrap();
let mut cases = vec![];
for i in vals {
cases.push(to_test_case(&i));
}
cases
}
pub fn match_string_to_err(s: &str) -> SPVError {
match s {
"Malformatted data. Read overrun" => SPVError::ReadOverrun,
"Read overrun" => SPVError::ReadOverrun,
"Vout read overrun" => SPVError::ReadOverrun,
"Vin read overrun" => SPVError::ReadOverrun,
"Read overrun when parsing vout" => SPVError::ReadOverrun,
"Read overrun when parsing vin" => SPVError::ReadOverrun,
"Bad VarInt in scriptPubkey" => SPVError::BadCompactInt,
"Bad VarInt in scriptSig" => SPVError::BadCompactInt,
"Read overrun during VarInt parsing" => SPVError::BadCompactInt,
"Malformatted data. Must be an op return" => SPVError::MalformattedOpReturnOutput,
"Maliciously formatted p2sh output" => SPVError::MalformattedP2SHOutput,
"Maliciously formatted p2pkh output" => SPVError::MalformattedP2PKHOutput,
"Maliciously formatted witness output" => SPVError::MalformattedWitnessOutput,
"Nonstandard, OP_RETURN, or malformatted output" => SPVError::MalformattedOutput,
"Header bytes not multiple of 80" => SPVError::WrongLengthHeader,
"Header does not meet its own difficulty target" => SPVError::InsufficientWork,
"Header bytes not a valid chain" => SPVError::InvalidChain,
"Hash is not the correct hash of the header" => SPVError::WrongDigest,
"MerkleRoot is not the correct merkle root of the header" => SPVError::WrongMerkleRoot,
"Prevhash is not the correct parent hash of the header" => SPVError::WrongPrevHash,
"Vin is not valid" => SPVError::InvalidVin,
"Vout is not valid" => SPVError::InvalidVout,
"Version, Vin, Vout and Locktime did not yield correct TxID" => SPVError::WrongTxID,
"Merkle Proof is not valid" => SPVError::BadMerkleProof,
"Reported length mismatch" => SPVError::OutputLengthMismatch,
_ => SPVError::UnknownError,
}
}
pub fn run_test<T>(test: T)
where
T: FnOnce(&serde_json::Value) -> () + panic::UnwindSafe,
{
let fixtures = setup();
let result = panic::catch_unwind(|| test(&fixtures));
assert!(result.is_ok())
}
#[test]
fn it_strips_0x_prefixes() {
let cases = [
("00", "00"),
("0x00", "00"),
("aa", "aa"),
("0xaa", "aa"),
("Quotidian", "Quotidian"),
("0xQuotidian", "Quotidian"),
];
for case in cases.iter() {
assert_eq!(strip_0x_prefix(case.0), case.1);
}
}
}