-
Notifications
You must be signed in to change notification settings - Fork 1
/
validate.rs
176 lines (160 loc) · 5.78 KB
/
validate.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
use anyhow::{anyhow, Result};
use banyan_shared::{eth::EthClient, proofs, proofs::window, types::*};
use log::info;
use rocket::serde::{Deserialize, Serialize};
use serde_json::from_str;
use std::io::Cursor;
use std::sync::Arc;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ChainlinkRequestData {
pub deal_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ResponseData {
pub deal_id: DealID,
pub success_count: u64,
pub num_windows: u64,
pub status: u16,
pub result: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ChainlinkResponse {
pub data: ResponseData,
}
pub struct WebserverState(pub Arc<EthClient>);
/* Function to construct an error response to return to Chainlink */
fn construct_error(deal_id: DealID, reason: String) -> ChainlinkResponse {
ChainlinkResponse {
data: ResponseData {
deal_id,
success_count: 0,
num_windows: 0,
status: 0,
result: reason,
},
}
}
/// this validates the deal based on a deal_id, returns a json response of either the success count and num_windows,
/// or an error message to be turned into Json<ChainlinkResponse> in the caller!
/// TODO fix logging... :|
pub(crate) async fn validate_deal_internal(
provider: Arc<EthClient>,
input_data: ChainlinkRequestData,
) -> Result<ChainlinkResponse> {
let deal_id = from_str(&input_data.deal_id)?;
let deal_info = provider
.get_offer(deal_id)
.await
.map_err(|e| anyhow!("Error in get_deal: {:?}", e))?;
// checking that deal is either finished or cancelled
let current_block_num = provider
.get_latest_block_num()
.await
.map_err(|e| anyhow!("Couldn't get most recent block number: {e}"))?;
// TODO: Why have any of these checks in the API. Shouldn't they all be in the Smart Contract Logic.
let deal_over = EthClient::deal_over(current_block_num, deal_info.clone());
let deal_cancelled = false; // TODO need to figure out how to get this
// this refuses to do the validation computations unless the deal is done with or cancelled
if !deal_over && !deal_cancelled {
return Ok(construct_error(deal_id, "Deal is ongoing".to_string()));
}
// this computes the actual deal length based on potential cancellation? and gets real number of windows :)
let agreed_upon_cancellation_block: BlockNum = BlockNum(0u64); // need to figure out how to get this
let deal_length_in_blocks = if deal_cancelled {
agreed_upon_cancellation_block - deal_info.deal_start_block
} else {
deal_info.deal_length_in_blocks
};
let num_windows =
window::get_num_windows(deal_length_in_blocks, deal_info.proof_frequency_in_blocks)
.map_err(|e| anyhow!("Could not get number of windows: {e}"))?;
// iterating over proof blocks (by window)
let mut success_count = 0;
for window_num in 0..num_windows {
let target_window_start = EthClient::compute_target_block_start(
deal_info.deal_start_block,
deal_info.proof_frequency_in_blocks,
window_num,
);
let target_block_hash = provider
.get_block_hash_from_num(target_window_start)
.await
.map_err(|e| anyhow!("Could not get block hash: {e}"))?;
let submitted_proof_in_block_num = match provider
.get_proof_block_num_from_window(deal_id, window_num as u64)
.await
.map_err(|e| {
anyhow!("Could not get block where proof was submitted for this window: {e}")
})? {
Some(block_num) => block_num,
None => {
info!("No proof submitted for window {}", window_num);
continue;
}
};
let proof_bytes: Vec<u8> = match provider
.get_proof_from_logs(submitted_proof_in_block_num, deal_id)
.await
.map_err(|e| {
anyhow!(
"Couldn't get log from block {}: {}",
submitted_proof_in_block_num.0,
e
)
})? {
Some(proof) => proof,
None => {
info!("Proof is too short for window {}", window_num);
continue;
}
};
let (chunk_offset, chunk_size) = proofs::compute_random_block_choice_from_hash(
target_block_hash,
deal_info.file_size.as_u64(),
);
// TODO is there an issue of coercing the Vec<u8> into a &[u8] here?
match EthClient::check_if_merkle_proof_is_valid(
Cursor::new(&proof_bytes),
deal_info.blake3_checksum.hash(),
chunk_offset,
chunk_size,
)
.map_err(|e| {
anyhow!(
"Error reading proof {}: {}",
submitted_proof_in_block_num.0,
e
)
})? {
true => {
info!("Proof succeeded for window {}", window_num);
success_count += 1;
}
false => {
info!("Proof failed for window {}", window_num);
continue;
}
}
}
if num_windows > 0 {
Ok(ChainlinkResponse {
data: ResponseData {
deal_id,
success_count,
num_windows: num_windows as u64,
status: 1,
result: "Ok".to_string(),
},
})
} else {
Ok(ChainlinkResponse {
data: ResponseData {
deal_id,
success_count,
num_windows: num_windows as u64,
status: 0,
result: "No windows found".to_string(),
},
})
}
}