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
use crate::get_scope_index;
use blake2b_simd::{blake2b, Hash};
use std::cmp::min;
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::path::Path;
use std::sync::mpsc;
use std::thread;
use std::time::Instant;
pub fn seek_file(
path: &Path,
start: usize,
end: usize,
previous_hash: &[u8],
target: &[u8],
time: u32,
multi: bool,
) -> Result<(u32, Vec<u8>), String> {
assert!(start < end);
assert_eq!(previous_hash.len(), 32);
assert_eq!(target.len(), 32);
let now = Instant::now();
let raw_fs = File::open(path).map_err(|err| err.to_string())?;
let mut fs = BufReader::new(raw_fs);
let scope_index = get_scope_index(previous_hash);
let start_pos = (scope_index * 32 * (end - start)) as u64;
fs.seek(SeekFrom::Start(start_pos))
.map_err(|err| err.to_string())?;
if multi {
let cache = prepare_cache(time, &previous_hash);
let (tx, rx) = mpsc::channel();
let cpu_count = num_cpus::get();
let step_size = (end - start) / cpu_count + 1;
let mut buffer = vec![0u8; step_size * 32];
let mut start_pos = start.clone();
for _ in 0..cpu_count {
match fs.read(&mut buffer) {
Ok(size) => {
let tx = tx.clone();
let buffer = buffer.clone();
let mut cache = cache.clone();
let target = target.to_vec();
let len = size / 32;
let end_pos = min(start_pos + len, end);
thread::spawn(move || {
for (index, nonce) in (start_pos..end_pos).enumerate() {
let scope_hash = &buffer[index * 32..index * 32 + 32];
let raw_work = poc_hash_from_scope(scope_hash, &mut cache);
let work_ref = &raw_work.as_bytes()[0..32];
if work_check(work_ref, &target) {
tx.send(Some((nonce as u32, work_ref.to_vec()))).unwrap();
return;
}
}
tx.send(None).unwrap();
});
start_pos += len;
}
Err(err) => return Err(err.to_string()),
}
}
let mut success = None;
for result in rx.iter().take(cpu_count) {
match result {
Some((nonce, work)) => {
if success.is_none() {
success.replace((nonce, work));
}
}
None => continue,
}
}
success.ok_or(format!(
"full seeked but not found enough work {}mSec",
now.elapsed().as_millis()
))
} else {
let mut buffer = [0u8; 32];
let mut cache = prepare_cache(time, previous_hash);
for nonce in start..end {
match fs.read(&mut buffer) {
Ok(32) => {
let raw_work = poc_hash_from_scope(&buffer, &mut cache);
let work_ref = &raw_work.as_bytes()[0..32];
if work_check(work_ref, target) {
return Ok((nonce as u32, work_ref.to_vec()));
}
}
Ok(size) => return Err(format!("wrong read size {} bytes", size)),
Err(err) => return Err(err.to_string()),
}
}
Err(format!(
"full seeked but not found enough work {}mSec",
now.elapsed().as_millis()
))
}
}
fn prepare_cache(time: u32, previous_hash: &[u8]) -> [u8; 4 + 32 + 32] {
let mut cache = [0u8; 4 + 32 + 32];
let time: [u8; 4] = time.to_le_bytes();
cache[0..4].clone_from_slice(&time);
cache[36..36 + 32].clone_from_slice(previous_hash);
cache
}
#[inline]
fn poc_hash_from_scope(scope_hash: &[u8], cache: &mut [u8; 4 + 32 + 32]) -> Hash {
cache[4..4 + 32].clone_from_slice(scope_hash);
blake2b(cache.as_ref())
}
#[inline]
fn work_check(work: &[u8], target: &[u8]) -> bool {
for (work, target) in work.iter().rev().zip(target.iter().rev()) {
if work > target {
return false;
} else if work < target {
return true;
} else {
continue;
}
}
false
}