1
1
use std:: time:: Duration ;
2
2
3
3
use anyhow:: Result ;
4
+ use blake2:: {
5
+ digest:: { Update , VariableOutput } ,
6
+ Digest ,
7
+ } ;
4
8
use reqwest:: Client ;
5
9
use serde:: { Deserialize , Serialize } ;
6
10
7
11
#[ derive( Serialize , Deserialize ) ]
8
12
pub struct PoolMetadataJson {
9
- pub ticker : String ,
10
13
pub name : String ,
11
14
pub description : String ,
15
+ pub ticker : String ,
12
16
pub homepage : String ,
13
17
}
14
18
15
- pub async fn fetch_pool_metadata ( url : String , timeout : Duration ) -> Result < PoolMetadataJson > {
19
+ impl TryFrom < & [ u8 ] > for PoolMetadataJson {
20
+ type Error = serde_json:: Error ;
21
+
22
+ /// Returns `PoolMetadataJson`
23
+ ///
24
+ /// # Arguments
25
+ ///
26
+ /// * `value` - Pool metadata (in json) as slice
27
+ fn try_from ( value : & [ u8 ] ) -> std:: result:: Result < Self , Self :: Error > {
28
+ serde_json:: from_slice :: < Self > ( value)
29
+ }
30
+ }
31
+
32
+ impl TryFrom < Vec < u8 > > for PoolMetadataJson {
33
+ type Error = serde_json:: Error ;
34
+
35
+ /// Returns `PoolMetadataJson`
36
+ ///
37
+ /// # Arguments
38
+ ///
39
+ /// * `value` - Pool metadata (in json) as bytes
40
+ fn try_from ( value : Vec < u8 > ) -> std:: result:: Result < Self , Self :: Error > {
41
+ PoolMetadataJson :: try_from ( value. as_ref ( ) )
42
+ }
43
+ }
44
+
45
+ /// Fetches pool metadata
46
+ ///
47
+ /// # Returns
48
+ ///
49
+ /// * `Ok<Vec<u8>>` - pool metadata in bytes format
50
+ pub async fn fetch_pool_metadata_as_bytes ( url : String , timeout : Duration ) -> Result < Vec < u8 > > {
16
51
let client = Client :: new ( ) ;
17
52
let response = client. get ( url) . timeout ( timeout) . send ( ) . await ?;
18
- let body = response. json :: < PoolMetadataJson > ( ) . await ?;
19
- Ok ( body)
53
+ let body = response. bytes ( ) . await ?;
54
+ Ok ( body. to_vec ( ) )
55
+ }
56
+
57
+ /// Verifies the calculated pool metadata hash, is similar to the expected hash
58
+ ///
59
+ /// # Arguments
60
+ ///
61
+ /// * `pool_metadata` - The pool metadata as bytes
62
+ /// * `expected_hash` - The expected hash of the `pool_metadata`
63
+ ///
64
+ /// # Returns
65
+ ///
66
+ /// * `Ok(())` - for successful verification
67
+ /// * `Err(<error description>)` - for failed verifaction
68
+ pub fn verify_pool_metadata_hash (
69
+ pool_metadata : & [ u8 ] ,
70
+ expected_hash : & acropolis_common:: types:: DataHash ,
71
+ ) -> Result < ( ) , String > {
72
+ // hash the serialized metadata
73
+ let mut hasher = blake2:: Blake2bVar :: new ( 32 ) . map_err ( invalid_size_desc) ?;
74
+ hasher. update ( pool_metadata) ;
75
+
76
+ let mut hash = vec ! [ 0 ; 32 ] ;
77
+ hasher. finalize_variable ( & mut hash) . map_err ( invalid_size_desc) ?;
78
+
79
+ if & hash == expected_hash {
80
+ return Ok ( ( ) ) ;
81
+ }
82
+
83
+ Err ( "pool metadata hash does not match to expected" . into ( ) )
20
84
}
21
85
86
+ fn invalid_size_desc < T : std:: fmt:: Display > ( e : T ) -> String {
87
+ format ! ( "Invalid size for hashing pool metadata json {e}" )
88
+ }
22
89
#[ cfg( test) ]
23
90
mod tests {
24
91
use super :: * ;
@@ -27,10 +94,29 @@ mod tests {
27
94
async fn test_fetch_pool_metadata ( ) {
28
95
let url = "https://raw.githubusercontent.com/Octalus/cardano/master/p.json" ;
29
96
let pool_metadata =
30
- fetch_pool_metadata ( url. to_string ( ) , Duration :: from_secs ( 3 ) ) . await . unwrap ( ) ;
97
+ fetch_pool_metadata_as_bytes ( url. to_string ( ) , Duration :: from_secs ( 3 ) ) . await . unwrap ( ) ;
98
+
99
+ let pool_metadata = PoolMetadataJson :: try_from ( pool_metadata) . expect ( "failed to convert" ) ;
100
+
31
101
assert_eq ! ( pool_metadata. ticker, "OCTAS" ) ;
32
102
assert_eq ! ( pool_metadata. name, "OctasPool" ) ;
33
103
assert_eq ! ( pool_metadata. description, "Octa's Performance Pool" ) ;
34
104
assert_eq ! ( pool_metadata. homepage, "https://octaluso.dyndns.org" ) ;
35
105
}
106
+
107
+ #[ tokio:: test]
108
+ async fn test_pool_metadata_hash_verify ( ) {
109
+ let url = " https://880w.short.gy/clrsp.json " ;
110
+
111
+ let expected_hash = "3c914463aa1cddb425fba48b21c4db31958ea7a30e077f756a82903f30e04905" ;
112
+ let expected_hash_as_arr = hex:: decode ( expected_hash) . expect ( "should be able to decode {}" ) ;
113
+
114
+ let pool_metadata =
115
+ fetch_pool_metadata_as_bytes ( url. to_string ( ) , Duration :: from_secs ( 3 ) ) . await . unwrap ( ) ;
116
+
117
+ assert_eq ! (
118
+ verify_pool_metadata_hash( & pool_metadata, & expected_hash_as_arr) ,
119
+ Ok ( ( ) )
120
+ ) ;
121
+ }
36
122
}
0 commit comments