1
+ mod utils;
2
+
3
+ use std:: collections:: HashMap ;
1
4
use std:: sync:: Arc ;
2
5
use std:: time:: { Duration , SystemTime } ;
3
6
4
7
use rocket:: tokio:: sync:: RwLock ;
8
+ use serde:: { Deserialize , Serialize } ;
5
9
10
+ use crate :: mix_nodes:: utils:: map_2_letter_to_3_letter_country_code;
6
11
use mixnet_contract:: MixNodeBond ;
7
12
use validator_client:: Config ;
8
13
14
+ pub ( crate ) type LocationCache = HashMap < String , Location > ;
15
+
16
+ #[ derive( Debug , Deserialize ) ]
17
+ pub ( crate ) struct GeoLocation {
18
+ pub ( crate ) ip : String ,
19
+ pub ( crate ) country_code : String ,
20
+ pub ( crate ) country_name : String ,
21
+ pub ( crate ) region_code : String ,
22
+ pub ( crate ) region_name : String ,
23
+ pub ( crate ) city : String ,
24
+ pub ( crate ) zip_code : String ,
25
+ pub ( crate ) time_zone : String ,
26
+ pub ( crate ) latitude : f32 ,
27
+ pub ( crate ) longitude : f32 ,
28
+ pub ( crate ) metro_code : u32 ,
29
+ }
30
+
31
+ #[ derive( Clone , Debug , JsonSchema , Serialize , Deserialize ) ]
32
+ pub ( crate ) struct Location {
33
+ pub ( crate ) two_letter_iso_country_code : String ,
34
+ pub ( crate ) three_letter_iso_country_code : String ,
35
+ pub ( crate ) country_name : String ,
36
+ pub ( crate ) lat : f32 ,
37
+ pub ( crate ) lng : f32 ,
38
+ }
39
+
40
+ impl Location {
41
+ pub ( crate ) fn new ( geo_location : GeoLocation ) -> Self {
42
+ let three_letter_iso_country_code = map_2_letter_to_3_letter_country_code ( & geo_location) ;
43
+ Location {
44
+ country_name : geo_location. country_name ,
45
+ two_letter_iso_country_code : geo_location. country_code ,
46
+ three_letter_iso_country_code,
47
+ lat : geo_location. latitude ,
48
+ lng : geo_location. longitude ,
49
+ }
50
+ }
51
+ }
52
+
53
+ #[ derive( Clone , Debug ) ]
54
+ pub ( crate ) struct MixNodeBondWithLocation {
55
+ pub ( crate ) location : Option < Location > ,
56
+ pub ( crate ) bond : MixNodeBond ,
57
+ }
58
+
9
59
#[ derive( Clone , Debug ) ]
10
60
pub ( crate ) struct MixNodesResult {
11
61
pub ( crate ) valid_until : SystemTime ,
12
- pub ( crate ) value : Vec < MixNodeBond > ,
62
+ pub ( crate ) value : HashMap < String , MixNodeBondWithLocation > ,
63
+ location_cache : LocationCache ,
13
64
}
14
65
15
66
#[ derive( Clone ) ]
@@ -21,23 +72,53 @@ impl ThreadsafeMixNodesResult {
21
72
pub ( crate ) fn new ( ) -> Self {
22
73
ThreadsafeMixNodesResult {
23
74
inner : Arc :: new ( RwLock :: new ( MixNodesResult {
24
- value : vec ! [ ] ,
75
+ value : HashMap :: new ( ) ,
25
76
valid_until : SystemTime :: now ( ) - Duration :: from_secs ( 60 ) , // in the past
77
+ location_cache : LocationCache :: new ( ) ,
26
78
} ) ) ,
27
79
}
28
80
}
29
81
82
+ pub ( crate ) fn new_with_location_cache ( location_cache : LocationCache ) -> Self {
83
+ ThreadsafeMixNodesResult {
84
+ inner : Arc :: new ( RwLock :: new ( MixNodesResult {
85
+ value : HashMap :: new ( ) ,
86
+ valid_until : SystemTime :: now ( ) - Duration :: from_secs ( 60 ) , // in the past
87
+ location_cache,
88
+ } ) ) ,
89
+ }
90
+ }
91
+
92
+ pub ( crate ) async fn get_location_cache ( & self ) -> LocationCache {
93
+ self . inner . read ( ) . await . location_cache . clone ( )
94
+ }
95
+
96
+ pub ( crate ) async fn set_location ( & self , identity_key : & str , location : Location ) {
97
+ let mut guard = self . inner . write ( ) . await ;
98
+
99
+ // cache the location for this mix node so that it can be used when the mix node list is refreshed
100
+ guard
101
+ . location_cache
102
+ . insert ( identity_key. to_string ( ) , location. clone ( ) ) ;
103
+
104
+ // add the location to the mix node
105
+ guard
106
+ . value
107
+ . entry ( identity_key. to_string ( ) )
108
+ . and_modify ( |item| item. location = Some ( location) ) ;
109
+ }
110
+
30
111
pub ( crate ) async fn get ( & self ) -> MixNodesResult {
31
112
// check ttl
32
- let valid_until = self . inner . clone ( ) . read ( ) . await . valid_until ;
113
+ let valid_until = self . inner . read ( ) . await . valid_until ;
33
114
34
115
if valid_until < SystemTime :: now ( ) {
35
116
// force reload
36
117
self . refresh ( ) . await ;
37
118
}
38
119
39
120
// return in-memory cache
40
- self . inner . clone ( ) . read ( ) . await . clone ( )
121
+ self . inner . read ( ) . await . clone ( )
41
122
}
42
123
43
124
pub ( crate ) async fn refresh_and_get ( & self ) -> MixNodesResult {
@@ -48,10 +129,21 @@ impl ThreadsafeMixNodesResult {
48
129
async fn refresh ( & self ) {
49
130
// get mixnodes and cache the new value
50
131
let value = retrieve_mixnodes ( ) . await ;
51
- self . inner . write ( ) . await . clone_from ( & MixNodesResult {
52
- value,
132
+ let location_cache = self . inner . read ( ) . await . location_cache . clone ( ) ;
133
+ * self . inner . write ( ) . await = MixNodesResult {
134
+ value : value
135
+ . into_iter ( )
136
+ . map ( |bond| {
137
+ let location = location_cache. get ( & bond. mix_node . identity_key ) . cloned ( ) ; // add the location, if we've located this mix node before
138
+ (
139
+ bond. mix_node . identity_key . to_string ( ) ,
140
+ MixNodeBondWithLocation { bond, location } ,
141
+ )
142
+ } )
143
+ . collect ( ) ,
53
144
valid_until : SystemTime :: now ( ) + Duration :: from_secs ( 60 * 10 ) , // valid for 10 minutes
54
- } ) ;
145
+ location_cache,
146
+ } ;
55
147
}
56
148
}
57
149
0 commit comments